home *** CD-ROM | disk | FTP | other *** search
/ Your Choice 3 / Your Choice Software Collection 3.iso / prgmming / qb_tips / qbtips_n.doc < prev    next >
Text File  |  1993-07-26  |  160KB  |  4,569 lines

  1.  
  2.  
  3. Name:             QBTips_N.Doc                         Date:  8/93
  4.  
  5. Also See:         QBTips_A through QBTips_M
  6.  
  7.  
  8. Purpose:          To provide insights and source code to help BASIC
  9.                   programmers -- beginner through advanced.
  10.  
  11.                   Load this into your word processor or editor.  Then
  12.                   scan it for tidbits you think will be useful.  Just
  13.                   "cut & paste" sections you like to separate files,
  14.                   then run the code.
  15.  
  16.  
  17. Source:           Below you'll find messages captured from the FidoNet
  18.                   Quik_Bas echo.  We captured CODE and significant tips,
  19.                   and eliminated chatter.
  20.  
  21.  
  22. Format:           Varies, depending on the author, their programming
  23.                   style, and the question or topic.
  24.  
  25.                   A form-feed (Chr$(12)) appears after most messages.
  26.                   This allows you to print this, and have each message
  27.                   (ie., each topic) start on a new page.
  28.  
  29.  
  30. Recommendation:   None!
  31.  
  32.                   Some of what you'll see below is brilliant.  Some
  33.                   demonstrates very poor programming techniques.  But
  34.                   all of it can prove useful if you have a need.
  35.  
  36.                   NOTE 1:
  37.  
  38.                   We have NOT tried all the code you see here, and some
  39.                   of it may not run as-is.  You may have to do a little
  40.                   editing to coax it.  One reason that code may not run
  41.                   is that messages sometimes get truncated or mangled in
  42.                   transmission.  Another reason is that authors make
  43.                   mistakes (or typos).  Again, we haven't tried running
  44.                   everything; but when you do, you'll probably quickly
  45.                   spot places that need editing.
  46.  
  47.                   NOTE 2:
  48.  
  49.                   There may be near-duplicate messages.  The original
  50.                   author may have refined the code, or may have found
  51.                   errors in the original.  If you see something that
  52.                   looks interesting, before you rely on the code, scan
  53.                   for the topic or author to see if a new set of code is
  54.                   below you -- more recent messages appear below. And
  55.                   note that the next message may be in a later package.
  56.  
  57.                   NOTE 3:
  58.  
  59.                   BEFORE running any code segment, scan through it and
  60.                   LOOK FOR code fragments which could be DISASTROUS!
  61.  
  62.                   *** We often run un-tested code fragments from a  ***
  63.                   *** RAM or floppy disk.  And BEFORE running it we ***
  64.                   *** scan for "c:" or "d:" (or other hard drive)   ***
  65.                   *** letters.  And we also scan for .. (see below) ***
  66.  
  67.  
  68.                   For example, scan for "OUT " -- and if you find any
  69.                   verify that the code is OUTting the correct values
  70.                   to the correct ports.  Typos, transmission errors
  71.                   or programmer mistakes could send the wrong values
  72.                   to the wrong ports.  At best, nothing will happen.
  73.                   At worst, you might fry your monitor -- or worse.
  74.  
  75.                   Also look for INTERRUPT (or INTERRUPTx).  These functions
  76.                   are v-e-r-y useful for invoking low-level DOS or BIOS
  77.                   functions.  But that low-level access also comes with
  78.                   some risks!  Programmer or transmission errors, open
  79.                   drive doors, etc., can, at best, cause your PC to hang.
  80.                   At worst, you could corrupt the FAT of your hard disk.
  81.  
  82.  
  83. =========================================================================
  84.  
  85.  
  86.  
  87.  
  88. ' From:  VICTOR YIU                Sent: 07-12-93 13:32
  89. '   To:  ALL                       Rcvd: -NO-
  90. '   Re:  BROWSER 1.0 NOTES
  91. '
  92. 'Hi, All!
  93. '
  94. 'Following two messages, I will present you Browser, a really quick hex
  95. 'file viewer, just like Calvin's Hexview, except using my SuperHex asm.
  96. 'procedure.  Capture the basic file (BROWSER.BAS), the PostIt! file to
  97. 'make the OBJ file, and then the two messages of the asm I wrote.
  98. '
  99. 'QB users must change all occurences of SSEG to VARSEG.
  100. '
  101. 'Enjoy!  And I want to know if it feels SNAPPY on your machine!
  102.  
  103. ' ==================== Browser 1.0 =======================
  104. ' by Victor Yiu, July 1993.  Released into Public Domain.
  105. '
  106. '              *** LOAD WITH /AH SWITCH ***
  107. ' This program is a binary file viewer, in hex and ascii,
  108. ' made to look like Calvin French's HEXVIEW, PCTools' View,
  109. ' Norton's DiskEdit, and numerous others.  It uses my public-
  110. ' domain SuperHex library, written in optimized assembly -- so
  111. ' it is FAST!
  112. '                      (to make EXE:)
  113. '       BC BROWSER /O;
  114. '       LINK /FARC /PACKC:64000 BROWSER+C:\QB\NOCOM,,,C:\QB\BCOM45+SUPERHEX;
  115. '            ^ (include "/EX" if you won't use LZEXE or PKLITE)
  116. '
  117. ' Compared to Clavin's HEXVIEW:
  118. '   o  Mine can open files up to 128k
  119. '   o  Mine is 5 million times faster (faster than PCTools or Norton)
  120. '   o  Has a built-in text filter
  121. '   o  Acceptable on an 4.77 MHz XT!
  122. '   o  Compiles to only 25K with LZEXE or PKLITE!
  123. '
  124. ' Next version will include search and editing features.
  125. '
  126. ' Speed comparison (on a 10MHz XT)*:
  127. '       Calvin French's HEXVIEW:   .55 K/sec
  128. '             PCTool's 4.x Edit:     3 K/sec
  129. '         Victor's SuperHEX (!):    20 K/sec   --->  WOW!
  130. '
  131. '  * I didn't do timing on my 486 because it was too darn fast!
  132. '
  133. ' By the way -- try loading a file from the floppy -- it's fun!
  134. ' ============================================================
  135.  
  136. DEFINT A-Z      ' $DYNAMIC
  137.  
  138. CONST Block = 8192, PageSize = 16 * 21, LastDataLine = 23
  139. CONST DataFG = 7, DataBG = 1, Attrib = DataFG + DataBG * 16
  140. CONST False = 0, True = NOT False
  141.  
  142. DECLARE SUB AdjustLastBytes (Row%)
  143. DECLARE SUB CheckPointer (Num&)
  144. DECLARE SUB DrawInterface ()
  145. DECLARE SUB DrawScreen ()
  146. DECLARE SUB LoadFile (LOFile&)
  147. DECLARE SUB PrintHex (Num&)
  148. DECLARE SUB ShowHex (Posit&)
  149. DECLARE FUNCTION GetAdapterSeg% ()
  150. DECLARE FUNCTION Signed% (Num&)
  151.  
  152. DECLARE SUB Scroll (BYVAL GoUp, BYVAL Attrib)
  153. DECLARE SUB MemCopy (BYVAL SegFrom, BYVAL OffFrom, BYVAL SegTo,_
  154.  BYVAL OffTo, BYVAL Leng)
  155. DECLARE SUB SuperHex (BYVAL VidSeg, BYVAL Row, BYVAL OffsetHex,_
  156.  BYVAL OffsetASCii, BYVAL segment, BYVAL Offset, BYVAL_
  157.  BackColor, BYVAL FilterOn)
  158. ' VidSeg     = Video segment of adapter (B800 for color, B000 for mono)
  159. ' Row        = Row to display data (1-25)
  160. ' OffsetHex  = Column to display the hex digits (1-)
  161. ' OffsetASCii= Column to display the characters themselves (1-)
  162. ' Segment    = Segment of source data
  163. ' Offset     = Offset of source data
  164. ' BackColor  = Background color in a packed byte:
  165. '                (BackGround * 16) + ForeGround
  166.  
  167. IF LEN(COMMAND$) = 0 THEN
  168.     PRINT "Syntax:"
  169.     PRINT "   BROWSER <filename>"
  170.     END
  171. END IF
  172.  
  173. DIM SHARED VidSegment, Pointer&, LastBox, LOFile&, FilterOn
  174.  
  175. OPEN COMMAND$ FOR BINARY AS #1
  176. LOFile& = LOF(1)
  177. IF (LOFile& + 3000 > FRE(-1)) OR (LOFile& > 131000) THEN PRINT "File too big.": END
  178. IF LOFile& = 0 THEN CLOSE : PRINT COMMAND$; " does not exist.":KILL COMMAND$: END
  179.  
  180. DIM SHARED Array&(LOFile& \ 4& + 4)
  181. Pointer& = 0: LastBox = 4: Null$ = CHR$(0)
  182.  
  183. CLS
  184. VidSegment = GetAdapterSeg
  185. DrawInterface
  186. LOCATE 12, 33: COLOR 31, 3
  187. PRINT " Loading file ... "; : COLOR 7
  188.  
  189. LoadFile LOFile&
  190.  
  191. IF LOFile& - 16 < PageSize THEN
  192.     LOCATE 12, 33: COLOR , 1
  193.     PRINT SPACE$(40);
  194. END IF
  195.  
  196. DrawScreen
  197. DO
  198.     DO: I$ = INKEY$
  199.     LOOP UNTIL LEN(I$)
  200.     IF LEN(I$) = 1 THEN I$ = UCASE$(I$)
  201.  
  202.     SELECT CASE I$
  203.         CASE Null$ + "Q", CHR$(13), " "   ' PageDown
  204.             Pointer& = Pointer& + PageSize
  205.             CheckPointer Pointer&
  206.         CASE Null$ + "I"        ' PageUp
  207.             Pointer& = Pointer& - PageSize
  208.             CheckPointer Pointer&
  209.         CASE Null$ + "P"        ' Down
  210.             IF Pointer& + PageSize < LOFile& THEN
  211.                 Scroll 0, LastDataLine
  212.                 Pointer& = Pointer& + 16
  213.                 ShowHex Pointer&
  214.  
  215.                 Temp& = Pointer& + PageSize - 16
  216.                 LOCATE LastDataLine, 3: COLOR 14
  217.                 PrintHex Temp&
  218.  
  219.                 SuperHex VidSegment, LastDataLine, 10, 62,_
  220.  Signed(sseg(Array&(0)) + Temp& \ 16), VARPTR(Array&(0)),_
  221.  Attrib, FilterOn
  222.                 IF LOFile& - Temp& < 15 THEN AdjustLastBytes_
  223.  LastDataLine
  224.             END IF
  225.         CASE Null$ + "H"        ' Up
  226.             IF Pointer& >= 16 THEN
  227.                 Scroll -1, LastDataLine
  228.                 Pointer& = Pointer& - 16
  229.                 ShowHex Pointer&
  230.  
  231.                 LOCATE 3, 3: COLOR 14
  232.                 PrintHex Pointer&
  233.                 SuperHex VidSegment, 3, 10, 62,_
  234.  Signed(sseg(Array&(0)) + Pointer& \ 16), VARPTR(Array&(0)),_
  235.  Attrib, FilterOn
  236.             END IF
  237.         CASE Null$ + "G"        ' Home
  238.             Pointer& = 0
  239.             DrawScreen
  240.         CASE Null$ + "O"        ' End
  241.             Pointer& = LOFile&
  242.             CheckPointer Pointer&
  243.         CASE "F"                ' toggle filter
  244.             FilterOn = NOT FilterOn
  245.             DrawScreen
  246.  
  247.             LOCATE 25, 58: COLOR 4, 3
  248.             IF FilterOn THEN PRINT CHR$(251);  ELSE PRINT " ";
  249.         CASE ELSE
  250.     END SELECT
  251. LOOP UNTIL I$ = CHR$(27)
  252.  
  253. COLOR 7, 0: CLS
  254. PRINT "Thanks for trying Browser 1.0!"
  255. PRINT
  256. END
  257.  
  258. REM $STATIC
  259. SUB AdjustLastBytes (Row)
  260.  
  261.     Remov = 16 - (LOFile& AND 15)
  262.     LOCATE Row, 58 - Remov * 3
  263.     PRINT SPACE$(Remov * 3 + 1);
  264.     LOCATE , 78 - Remov
  265.     PRINT SPACE$(Remov);
  266.  
  267. END SUB
  268.  
  269. SUB CheckPointer (Num&)
  270.  
  271. IF Num& + PageSize - 15 > LOFile& THEN
  272.     Num& = LOFile& + 15 - PageSize
  273. END IF
  274.  
  275. IF Num& < 0 THEN Num& = 0
  276. DrawScreen
  277.  
  278. END SUB
  279.  
  280. SUB DrawInterface
  281.  
  282. COLOR 14, 12
  283. PRINT " Browser 1.0  ■  by Victor Yiu, July 1993  ■  Idea from"+_
  284. " Calvin French's HEXVIEW "
  285. COLOR 15, 1
  286. PRINT CHR$(218); CHR$(196); CHR$(180);
  287. COLOR 15, 3: PRINT " "; COMMAND$; " "; : COLOR 15, 1
  288. PRINT CHR$(195); STRING$(80 - POS(0), 196); CHR$(191)
  289.  
  290. FOR Lin = 3 TO LastDataLine
  291.     LOCATE Lin, 1: PRINT CHR$(179); SPACE$(78);
  292.  
  293.     IF Lin = 3 THEN
  294.         PRINT CHR$(24);
  295.     ELSEIF Lin = LastDataLine THEN
  296.         PRINT CHR$(25);
  297.     ELSE
  298.         PRINT CHR$(176);
  299.     END IF
  300. NEXT
  301.  
  302. LOCATE 4, 80, 0: PRINT CHR$(219);
  303. LOCATE 24, 1: PRINT CHR$(192); CHR$(196); CHR$(180);
  304. COLOR 13: PRINT "       (     )/"; LTRIM$(STR$(LOF(1)));
  305. COLOR 15: PRINT " "; CHR$(195); STRING$(80 - POS(1), 196);_
  306.  CHR$(217);
  307.  
  308. LOCATE 25, 1: COLOR 14, 3
  309. PRINT " Adjust Viewport "; : COLOR 15
  310. PRINT "[PgUp/PgDn] [Up/Down] [Home/End]         [F]=Filter"+_
  311. " [Esc]=Quit ";
  312.  
  313. END SUB
  314.  
  315. SUB DrawScreen
  316. STATIC NotOnePage, L    ' 1=True, 2=False, 0=first call
  317.  
  318. IF NotOnePage = 0 THEN
  319.     IF LOFile& - 16 < PageSize THEN
  320.         NotOnePage = 1
  321.         L = (LOFile& - 1) \ 16 + 3
  322.     ELSE
  323.         NotOnePage = 2
  324.         L = LastDataLine
  325.     END IF
  326. END IF
  327.  
  328. ShowHex Pointer&
  329. COLOR 14
  330.  
  331. Temp& = Pointer&
  332. FOR Row = 0 TO L - 3
  333.     SuperHex VidSegment, Row + 3, 10, 62, Signed(sseg(Array&(0))_
  334.  + Temp& \ 16), VARPTR(Array&(0)), Attrib, FilterOn
  335.  
  336.     LOCATE Row + 3, 3, 0
  337.     PrintHex Temp&
  338.  
  339.     Temp& = Temp& + 16
  340. NEXT
  341.  
  342. IF Temp& > LOFile& THEN AdjustLastBytes L
  343.  
  344. END SUB
  345.  
  346. FUNCTION GetAdapterSeg
  347.     DEF SEG = 0
  348.     ColorM = (PEEK(&H410) AND 48) <> 48
  349.     DEF SEG 'Monocrome--^                       ^
  350.             'Color Graphics Adapter or better --|
  351.  
  352.     IF ColorM THEN GetAdapterSeg = &HB800 ELSE GetAdapterSeg = &HB000
  353. END FUNCTION
  354.  
  355.  
  356. SUB LoadFile (LOFile&)
  357.  
  358. Dummy& = FRE("")
  359.  
  360. TempStor$ = SPACE$(Block)
  361. Start& = sseg(Array&(0))
  362.  
  363. FOR LoadUp = 1 TO LOFile& \ Block
  364.     GET #1, , TempStor$
  365.     MemCopy sseg(TempStor$), SADD(TempStor$), Signed(Start&), VARPTR(Array&(0)), Block
  366.     Start& = Start& + Block \ 16
  367.     ShowHex LoadUp * 1& * Block
  368. NEXT
  369.  
  370. TempStor$ = SPACE$(LOFile& MOD Block)
  371. GET #1, , TempStor$
  372. MemCopy sseg(TempStor$), SADD(TempStor$), Signed(Start&), 0, LEN(TempStor$)
  373.     ' *** SSEG to sseg for QB/QBASIC users!
  374. END SUB
  375.  
  376. SUB PrintHex (Num&)
  377.  
  378.     'PRINT MID$(HEX$(Num& + &H100000), 2);
  379.     PRINT RIGHT$("0000" + HEX$(Num&), 5);
  380.  
  381. END SUB
  382.  
  383. SUB ShowHex (Posit&) STATIC
  384.  
  385. COLOR 15, 1
  386. LOCATE 24, 5
  387. PRINT USING "######"; Posit&;
  388. LOCATE , 12
  389. PrintHex Posit&
  390.  
  391. LOCATE LastBox, 80
  392. PRINT CHR$(176);
  393. LastBox = Posit& * 18 \ LOFile& + 4
  394. LOCATE LastBox, 80
  395. PRINT CHR$(219);
  396.  
  397. END SUB
  398.  
  399. FUNCTION Signed (Num&)
  400.  
  401.     IF Num& > 32767 THEN
  402.         Signed = Num& - 65536
  403.     ELSE
  404.         Signed = Num&
  405.     END IF
  406.  
  407. END FUNCTION
  408.  
  409.  
  410. '
  411. Msg #:  693                       QUIKBAS Subboard
  412.  From:  VICTOR YIU                Sent: 07-12-93 13:40
  413.    To:  ALL                       Rcvd: -NO-
  414.    Re:  BROWSER 1.0 [OBJ] 1/1
  415.  
  416. ' =========== Notice: this PostIt! script can't be extracted
  417. ' by the new version of PostIt! posted by Rich.  You'll have to
  418. ' run this through QB.
  419. '
  420. DEFINT A-Z:DIM SHARED B,K,S,B&,Z&:XA '** by PostIt! 7.0 **
  421. SUB XA:OPEN "O",1,"SUPERHEX.OBJ",4^6:Z&=321:?STRING$(50,177);
  422. U"&O/%-%xzuj#wmj'*se,%%%)ht#ijigd,%7f%%''&(,_I%%%&-x%zujw.mj'%%%%
  423. U",r%jrht:u(+%%%+xh%wtqqBI%%&,oj%&#%%zZKeC%&'=k7m8C),uuo\JYYX;>EL
  424. U"/%Y5U-ZsZ\-3pJT\(tRAM+%DjG%z%kz%z%kz%z%kz%z%kz%z%kz%z%kz%z%kz%z
  425. U"%bzd?275%4E;M_%]*QK.]EqN*z?[md*f)Sz[3fZ-5=pJ\&WSZs-B25%Y-U3,.B6
  426. U"UU_o05%-=%op_E?M(_a';__\M''[j&aYuPY\zY<#z)E;zRr.nC)[Qfd(z[3L.-D
  427. U",,C5%:zZeCV%&uZRs+=M4->E16JbB&Pslt.e-D,C#/%zZ3eYM+B1&+OAM-%Cs'w
  428. U"=27''3qF;F5,BC)%R&Y'%%&C"
  429. END SUB
  430. CLOSE:?:IF S=171AND B&=Z&THEN?":) Ok!"ELSE?":( Bad!
  431. SUB U(A$):FOR A=1TO LEN(A$):C=ASC(MID$(A$,A))-37:B=B-1:IF C<0THEN
  432. C=91+C*32
  433. S=(S+C)*2:IF B<0THEN B=4:K=C ELSE?#1,CHR$(C+(K MOD
  434. 3)*86);:K=K\3:B&=B&+1
  435. S=S\256+(S AND 255):NEXT:LOCATE,1:?STRING$(B&*50\Z&,219);:END SUB
  436.  
  437. ' To make a LIB and QLB out of this file:
  438. '
  439. ' LIB SUPERHEX +SUPERHEX;
  440. ' LINK /QUI SUPERHEX.LIB,,,c:\qb\bqlb45;
  441. '                          qbxqlb        for PDS (I think)
  442. '                          vbdosqlb      for VBDOS
  443. ' Load into QB:
  444. '    QB BROWSER /L SUPERHEX /AH
  445. '
  446. ' Instructions on how to make EXE file is in the BASIC source.
  447. '
  448. '
  449.  
  450. Msg #:  694                       QUIKBAS Subboard
  451.  From:  VICTOR YIU                Sent: 07-12-93 13:43
  452.    To:  ALL                       Rcvd: -NO-
  453.    Re:  BROWSER 1.0 [ASM] 1/2
  454.  
  455. ; All:
  456. ;
  457. ; This code is different from my original posting of SuperHex
  458. ; very slightly only.  I added a mem. copy and a scrolling
  459. ; procedure.
  460. ;              -- Victor
  461.  
  462. ; =========== SuperHEX 1.1 ===============
  463. ; by Victor Yiu, July 1993
  464. ;
  465. ; Ultra-fast ASCII to HEX conversion...
  466. ; designed for calling up from a file
  467. ; or memory viewer to display hex/ascii
  468. ; codes like Norton's DiskEdit, or PCTool's
  469. ; View... except much faster!
  470. ;
  471. ; This code is released into public domain.
  472. ; =========================================
  473.  
  474. CODE SEGMENT PARA PUBLIC 'CODE'
  475.     PUBLIC SuperHex, MemCopy, Scroll
  476.     ASSUME CS:code, DS:nothing, ES:nothing, SS:nothing
  477.  
  478. CharsPerLine    = 80        ; set-up hard coded constant
  479. FilterReplaceChar   EQU '.' ; replace bad chars. with what?
  480.  
  481. SuperHex PROC FAR
  482.     PUSH BP             ; set up stack frame
  483.     MOV BP, SP
  484.     PUSH DS             ; save registers
  485.     PUSH SI
  486.     PUSH DI
  487. ; =============================================================
  488. ;VidSeg, Row, OffsetHex, OffsetASCii, Seg:Off memory (16 byte)
  489. ; BP+20  BP+18  BP+16       BP+14            BP+10 [DWORD]
  490.  
  491. ; BG Color    Filter
  492. ;   BP+8       BP+6
  493. ; =============================================================
  494.  
  495.     LES AX, SS:[BP+18]  ; VidSeg --> ES
  496.     DEC AX              ; Row --> AX  (adjust to 0-24 range)
  497.     JZ NoMul            ; if 0, don't multiply to save time
  498.     MOV BL, CharsPerLine; get # chars per line
  499.     MUL BL              ; multiply
  500. NoMul:
  501.     SHL AX, 1           ; *2: vid.mem. alternates ASCii/color
  502.     MOV DX, AX          ; save into DX
  503.  
  504.     LDS SI, SS:[BP+10]  ; get source memory into DS:SI
  505.     PUSH SI             ; save it for later
  506.  
  507. ; ====== Setup to write the 16 bytes of ASCii first
  508.     MOV AH, SS:[BP+8]   ; get attribute into AH
  509.     MOV DI, DX          ; move start of row offset into DI
  510.     MOV BX, SS:[BP+14]  ; get offset of ASCii
  511.     DEC BX
  512.     SHL BX, 1           ; *2 because of vid. mem.
  513.     ADD DI, BX          ; compute final offset
  514.     CMP WORD PTR [BP+6], 0       ; filter on?
  515.     JNE FilterOn
  516.  
  517. REPT 16                 ; repeat 16 times
  518.     LODSB               ; get byte
  519.     STOSW               ; store byte + attribute
  520. ENDM
  521.     JMP SHORT Continue
  522. EVEN
  523. FilterOn:
  524.     MOV CX, 16
  525.     MOV BX, '~ '        ; preload constants
  526. EVEN
  527. FilterTop:
  528.     LODSB
  529.     CMP AL, BL          ; below 32?
  530.     JL NoShow
  531.     CMP AL, BH          ; more than 127
  532.     JG NoShow
  533.     STOSW
  534.     LOOP FilterTop
  535.     JMP SHORT Continue
  536. EVEN
  537. NoShow:
  538.     MOV AL,FilterReplaceChar
  539.     STOSW
  540.     LOOP FilterTop
  541.  
  542.  
  543. ; ======= Set up for HEX conversion to screen
  544. Continue:
  545.     MOV BX, SS:[BP+16]  ; get offset of HEX
  546.     DEC BX
  547.     SHL BX, 1           ; *2 for vid. mem
  548.     ADD DX, BX          ; add to original row offset
  549.     MOV DI, DX          ; put it into the index register
  550.     POP SI              ; get previous SI
  551.     MOV CX, 16          ; do sixteen characters
  552.  
  553.     MOV BL, AH          ; attribute into BL
  554.  
  555.     MOV DX, (256*9) + ('A'-'9'-1) 
  556.     MOV BP, '00'        ; preload stuff to make it scream
  557.  
  558. ; BP = '00'
  559. ; BL = attribute
  560. ; BH = -- reserved for temporary digit
  561. ; DL = 'A'-'9'-1
  562. ; DH = 9
  563. EVEN
  564. LoopTop:
  565.     MOV BH, 16          ; load divisor
  566.     LODSB               ; get character
  567.     MOV AH, 0           ; clear AH
  568.     DIV BH              ; to get tens in AL, ones in AH.
  569.     CMP AL, DH          ; > '9'?
  570.     JLE NextDigit       ; no -- don't fix
  571. EVEN
  572.     ADD AL, DL          ; fix it
  573. NextDigit:
  574.     CMP AH, DH          ; > '9'?
  575.     JLE WriteOut        ; no -- don't fix
  576.     ADD AH, DL          ; fix it
  577. WriteOut:
  578.     ADD AX, BP          ; add '00' to digits to make them ASCii
  579.     MOV BH, AH          ; save ones digit for next character
  580.     MOV AH, BL          ; get attribute
  581.     STOSW               ; write digit
  582.     MOV AL, BH          ; get next
  583.     STOSW               ; write
  584.     MOV AL, ' '         ; write space
  585.     STOSW
  586.     CMP CX, 9           ; between the 8th and 9th HEX digits
  587.     JE AddSpace
  588.     LOOP LoopTop
  589.     JMP SHORT OttaHere
  590. EVEN
  591. AddSpace: STOSW
  592.     LOOP LoopTop
  593.  
  594. OttaHere:
  595.     POP DI              ; restore registers
  596.     POP SI
  597.     POP DS
  598.     POP BP
  599.     RET 16       ; shave off 16 bytes of passed in parameters
  600. SuperHex ENDP
  601.  
  602. MemCopy PROC FAR
  603.     PUSH BP
  604.     MOV BP, SP          ; set up stack frame
  605.     PUSH DS
  606.     PUSH SI
  607.     PUSH DI
  608.  
  609.     CLD
  610.     MOV CX, [BP+6]      ; # to copy in CX
  611.     LES DI, [BP+8]      ; get dest.
  612.     LDS SI, [BP+12]     ; get source
  613.  
  614.     SHR CX, 1           ; odd byte
  615.     JNC CopyStart
  616.     MOVSB
  617. CopyStart: REP MOVSW    ; do copy
  618.  
  619.     POP DI
  620.     POP SI
  621.     POP DS
  622.     POP BP
  623.     RET 10
  624. MemCopy ENDP
  625.  
  626. Scroll PROC FAR
  627.     PUSH BP
  628.     MOV BP, SP      ; set up stack frame
  629.  
  630. ; BP+8 = MoveUp?   BP+6 = attribute
  631.  
  632.     MOV BH, [BP+6]  ; load up attribute
  633.     MOV AX, 0601h   ; 1 line
  634.     CMP BYTE PTR [BP+8], 0    ; zero?
  635.     JE Down        ; yes; go up
  636.     INC AH          ; go down (AX=0701)
  637. Down:
  638.     MOV CX, 0202h   ; (3,3) top left
  639.     MOV DX, 0164Ch  ; (23,77) bottom right
  640.     INT 010h        ; call vid. interrupt
  641.     POP BP
  642.     RET 4
  643.  
  644. Scroll ENDP
  645.     CODE ENDS
  646. END
  647.  
  648. '
  649.  
  650.  
  651.  From:  AARON LAPIKAS             Sent: 07-07-93 17:02
  652.    To:  MARK PRUITT               Rcvd: -NO-
  653.    Re:  CRCS
  654.  
  655. Hi Mark!
  656.  
  657.  > My questions are:
  658.  
  659. Only three?  :)
  660.  
  661. > Does a '32 bit CRC' turn out the same no matter who's code generates it?
  662. > In other words... is there only ONE '32 bit CRC' for a given chunk o' data?
  663.  
  664. As far as I know, yes.  If it weren't that way, how could it be used as
  665. a method of error checking?
  666.  
  667. > Will the 32 bit CRC of my name turn out completely unique from the other
  668. > several hundred users of the same bbs?  Since I have NO idea of even what a
  669. > 32 bit CRC is, much less the math that goes into it, it seems to me that a
  670.                                    ...
  671.  
  672. Yes.  The chances of having two different pieces of data with the same
  673. CRC are very small, if not next to none.
  674.  
  675.  > Could someone post "their way of calculating a 32 bit CRC" with a
  676.  > layman's (read that: lame brain's) explanation of what the code is
  677.  > doing?
  678.  
  679. '-----------------------------------------------------------------------
  680.  
  681. DECLARE FUNCTION CRC32% (Target$)
  682.  
  683. DEFINT A-Z
  684. FUNCTION CRC32 (B$)
  685.  
  686. DIM CRC AS LONG
  687. CRC = 0
  688.  
  689. FOR I = 1 TO LEN(B$)                           'Calculate for Length of
  690. Block
  691.   ByteVal = ASC(MID$(B$, I, 1))
  692.  
  693.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 128) = 128)
  694.   CRC = ((CRC AND 32767&) * 2&)
  695.   IF TestBit THEN CRC = CRC XOR &H8005&
  696.  
  697.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 64) = 64)
  698.   CRC = ((CRC AND 32767&) * 2&)
  699.   IF TestBit THEN CRC = CRC XOR &H8005&
  700.  
  701.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 32) = 32)
  702.   CRC = ((CRC AND 32767&) * 2&)
  703.   IF TestBit THEN CRC = CRC XOR &H8005&
  704.  
  705.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 16) = 16)
  706.   CRC = ((CRC AND 32767&) * 2&)
  707.   IF TestBit THEN CRC = CRC XOR &H8005&
  708.  
  709.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 8) = 8)
  710.   CRC = ((CRC AND 32767&) * 2&)
  711.   IF TestBit THEN CRC = CRC XOR &H8005&
  712.  
  713.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 4) = 4)
  714.   CRC = ((CRC AND 32767&) * 2&)
  715.   IF TestBit THEN CRC = CRC XOR &H8005&
  716.  
  717.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 2) = 2)
  718.   CRC = ((CRC AND 32767&) * 2&)
  719.   IF TestBit THEN CRC = CRC XOR &H8005&
  720.  
  721.   TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 1) = 1)
  722.   CRC = ((CRC AND 32767&) * 2&)
  723.   IF TestBit THEN CRC = CRC XOR &H8005&
  724. NEXT I
  725.  
  726. CRC32% = CRC
  727.  
  728. CRCHigh% = (CRC \ 256)                          'Break Word Down into
  729. Bytes
  730. CRCLow% = (CRC MOD 256)                         'for Comparison Later
  731. ComputeCRC& = CRC                               'Return the Word Value
  732.  
  733. END FUNCTION
  734. '-----------------------------------------------------------------------
  735.  
  736. '
  737.  
  738.  From:  STEVE DEMO                Sent: 07-05-93 15:32
  739.    To:  PAUL SENECHKO             Rcvd: -NO-
  740.    Re:  (R)DIRECTORIES IN QB 4.
  741.  
  742.  -=> Quoting Bill Smith to Paul Senechko <=-
  743.  
  744.  PS>Hi all, I am looking for a way in Quick Basic 4.5 to put all of the
  745.  PS>directories on the current drive into a file.  Any ideas?
  746.  
  747.  BS> How about SHELL TREE>file.nam
  748.  
  749.  Ha, Ha, Ha, Ha, Ha,  That's a good one. Now Listen up Paul I didn't
  750. write this it's about as HOT as the day is :-). I converted it back to
  751. QB for ya.
  752.  
  753. Steve Demo
  754.  
  755. '$INCLUDE: 'qb.bi'
  756. DECLARE SUB Tree (drive$, Count!, Array$())
  757. DEFINT A-Z
  758. CONST DOS = &H21
  759. CONST SetDTA = &H1A00, FindFirst = &H4E00, FindNext = &H4F00  ' These
  760. Const are for Tree sub
  761. DIM Array$(500)
  762. Tree "C:\", Count!, Array$()
  763. FOR x = 1 TO Count! STEP 1
  764.   IF Array$(x) = "" THEN END
  765.   PRINT Array$(x)
  766. NEXT x
  767.  
  768. SUB Tree (drive$, Count!, Array$())
  769.  
  770. DIM r AS RegTypeX
  771. search$ = drive$                                   'reassign string
  772. since it
  773. IF Count! = 0 THEN                                  ' gets changed in
  774. routine
  775.   search$ = UCASE$(search$)                        'make upper case
  776.   IF LEN(search$) = 1 THEN search$ = search$ + ":" 'define search
  777.   search$ = LEFT$(search$, 2)
  778.   Count! = 1
  779.   Array$(Count!) = search$ + "\"
  780. END IF
  781.  
  782. zero$ = CHR$(0): DTA$ = SPACE$(43) '43    'define ZERO and DTA
  783. search$ = search$ + "\"
  784. srch$ = search$ + "*" + zero$          'dos requires zero terminated
  785. string
  786.  
  787. 'get original dta
  788. r.ax = &H2F00
  789. INTERRUPTX &H21, r, r
  790. sgmt = r.es: ofst = r.bx               'save segment and offset of dta
  791. mode = &H4E00                          'set mode to FINDFIRST
  792.  
  793. 'set our dta
  794. DO
  795.  r.ax = &H1A00
  796.  r.ds = VARSEG(DTA$): r.dx = SADD(DTA$)  'change SSEG to VARSEG in qb
  797.  INTERRUPTX &H21, r, r                 'tell dos where this dta is
  798.  
  799.  r.ax = mode                           'findfirst, or findnext
  800.  r.cx = 16                             'look for directories
  801.  r.ds = VARSEG(srch$): r.dx = SADD(srch$)'change SSEG to VARSEG in qb
  802.  INTERRUPTX &H21, r, r                 'find one
  803.  IF (r.flags AND 1) THEN EXIT DO       'if none, bail
  804.  f.attr = ASC(MID$(DTA$, 22))          'attribute in f.attr
  805.  tmp$ = MID$(DTA$, 31) + zero$
  806.  f.name$ = LEFT$(tmp$, INSTR(tmp$, zero$) - 1)  'directory name in
  807. f.name
  808.  mode = &H4F00                         'change mode to FINDNEXT
  809.  IF ASC(f.name$) <> 46 THEN            'we don't want '.' or '..'
  810.    IF f.attr = 16 THEN                 'make sure it's a directory
  811.   Count! = Count! + 1                 'increment Count!
  812.   s$ = search$ + f.name$            'full path name
  813.   Array$(Count!) = s$                'add to array
  814.   Tree s$, Count!, Array$()     'look for some dirs here
  815.    END IF
  816.  END IF
  817. LOOP
  818. r.ax = &H1A00
  819. r.ds = sgmt: r.dx = ofst               'return original dta segment &
  820. offset
  821. INTERRUPTX &H21, r, r
  822. END SUB
  823. '
  824.  
  825. 'Msg #:  730                       QUIKBAS Subboard
  826. ' From:  SCOTT WUNSCH              Sent: 07-11-93 10:48
  827. '   To:  ALL                       Rcvd: -NO-
  828. '   Re:  READBAS1.BAS
  829. '
  830. 'Salutations, All!
  831. '
  832. '  This might come in handy to someone here...
  833. '
  834. '  Area: NET140.TECH
  835. '  From: Frank Cox, 1:140/53 (10 Jul 93 10:18)
  836. '  To:   Whoever cares
  837. '  Subj: READBAS1.BAS
  838. '__________O_/________________/ SNIP \__________________\_O__________
  839. '          O \                \ HERE /                  / O
  840. 'This just came around in the DR_DEBUG echo and I thought it was worth
  841. 'passing along.
  842. '
  843. 'Problem:  You have some GWBASIC source code which has been saved in
  844. 'tokenized format, and you don't have a copy of GWBASIC to use to
  845. 'convert it into an ASCII file for use under QBASIC or whatever.  This
  846. 'is a common problem as GWBASIC is not distributed with MS-DOS 5.0 and
  847. 'up.
  848.  
  849. 'Solution:  This old program (which appears to run fine under QBASIC):
  850.  
  851. 1 ''            READBAS 1.1 -  READS BASIC PROGRAMS SAVED IN BINARY
  852. 2 ''        NELSON FORD (713) 960-1300   (713) 721-6104    APRIL 11,1985
  853. 3 ''
  854. 4 '' PUBLIC DOMAIN.  The idea is to compile this program and use it while in
  855. 5 '' DOS to look at BASIC programs that have been saved in binary format.  The
  856. 6 '' compiled version of this is not being uploaded due to the inordinate
  857. 7 '' amount of difficulty and expense required to make a go
  858. 10 DEFINT A-Z:  CLS: INPUT "FILE NAME"; FI$
  859. 20 INPUT "TO (1)SCREEN (2)PRINTER (3)DISK"; D
  860. 30 IF D=1 THEN F2$="SCRN:" ELSE IF D=2 THEN F2$="LPT1:" ELSE IF D=3
  861. THEN INPUT_ "OUTPUT FILENAME"; F2$ ELSE 20
  862. 40 DIM X#(8): PRINT: PRINT "PRESS ANY KEY TO ABORT": PRINT
  863. 50 DIM T$(115), T3$(6), T4$(30), T5$(37)
  864. 60 FOR T=129 TO 243: READ T$(T-128):  NEXT   'tokens 129-243
  865. 70 FOR T=129 TO 134: READ T3$(T-128): NEXT   'token 253 followed by 129-134
  866. 80 FOR T=129 TO 158: READ T4$(T-128): NEXT   'token 254 followed by 129-158
  867. 90 FOR T=129 TO 165: READ T5$(T-128): NEXT   'token 255 followed by 129-165
  868. 95 '
  869. 100 OPEN FI$ AS 1 LEN=1:  FIELD 1, 1 AS X$
  870. 110 OPEN F2$ FOR OUTPUT AS #2:  GET 1
  871. 120 IF ASC(X$) <>255 THEN PRINT "NOT A BASIC PROGRAM SAVED IN BINARY":END
  872. 125 '----get, print line number:
  873. 130 GET 1: X=ASC(X$): GET 1: IF X=0 AND ASC(X$)=0 THEN STOP
  874. 140 GET 1: N$=STR$(ASC(X$)):  GET 1: X=ASC(X$)
  875. 150 IF X>0 THEN N$=STR$(X*256+VAL(N$))
  876. 160 PRINT #2, RIGHT$(N$,LEN(N$)-1) " ";
  877. 190 '----get a hex character and translate:
  878. 200 GET 1: X= ASC(X$)
  879. 210 U$=INKEY$: IF U$<>"" THEN END
  880. 220 IF X=58 THEN GET 1: X=ASC(X$): IF X=143 THEN GOSUB 910: GOTO 130 ELSE IF _ X<>161 THEN PRINT #2,":";
  881. 230 IF X=0  THEN PRINT #2,"": GOTO 130    'ascii 0 marks end of BASIC line
  882. 240 IF X>31 THEN 300  ELSE IF X <11 THEN STOP
  883. 250   ON X-10 GOSUB
  884. 400,440,480,500,540,580,600,600,600,600,600,600,600,600, 600,600,640,660,720,815 ,820
  885. 260   GOTO 200
  886. 270   RETURN
  887. 290 '------
  888. 300 IF X <128 THEN PRINT #2, X$;:  GOTO 200
  889. 310 IF X >128 AND X <244 THEN PRINT #2, T$(X-128);:  GOTO 200
  890. 320 IF X >252 AND X <256 THEN GET 1: Y=ASC(X$) ELSE 200
  891. 330 IF Y <129 THEN PRINT "ERROR IN FILE": STOP
  892. 340 ON X-252 GOTO 350,360,370:  GOTO 200
  893. 350   PRINT #2, T3$(Y-128);:  GOTO 200
  894. 360   PRINT #2, T4$(Y-128);:  GOTO 200
  895. 370   PRINT #2, T5$(Y-128);:  GOTO 200
  896. 390 '
  897. 400 GET 1: N=X: GET 1: N=X*256 +N    '11  =  OCTAL
  898. 410 PRINT #2, "&O" OCT$(N);
  899. 420 RETURN
  900. 430 '
  901. 440 GET 1: N=X: GET 1: N=X*256 +N    '12  =  HEX
  902. 450 PRINT #2, "&H" HEX$(N);
  903. 460 RETURN
  904. 470 '
  905. 480 STOP                             '13  NOT USED
  906. 490 '
  907. 500 GET 1: N$=STR$(ASC(X$))          '14  INTEGERS
  908. 505 GET 1: X=ASC(X$)
  909.  
  910. 510 IF X>0 THEN N$=STR$(X*256+VAL(N$))
  911. 520 PRINT #2, RIGHT$(N$,LEN(N$)-1);
  912. 530 RETURN
  913. 535 '
  914. 540 GET 1: N$=STR$(ASC(X$))          '15  =  NUMBERS 10 TO 255
  915. 550 PRINT #2, RIGHT$(N$,LEN(N$)-1);
  916. 560 RETURN
  917. 570 '
  918. 580 STOP                             '16  NOT USED
  919. 590 '
  920. 600 N$=STR$(X-17)                    '17 - 26 = NUMBERS 0 TO 9
  921. 610 PRINT #2, RIGHT$(N$,LEN(N$)-1);
  922. 620 RETURN
  923. 630 '
  924. 640 STOP                             '27  NOT USED
  925. 650 '
  926. 660 GET 1: N=ASC(X$): GET 1          '28  =  NUMBERS > 255 AND <32267
  927. 670 N$= STR$(256*ASC(X$) +N)
  928. 680 PRINT #2, RIGHT$(N$,LEN(N$)-1);
  929. 690 RETURN
  930. 700 '
  931. 710                                  '29  =  NUMBERS >32267 AND < ?
  932. 720 FOR I=1 TO 4: GET 1: X#(I)=ASC(X$): NEXT:  Z$=""
  933. 730   FOR J=3 TO 1 STEP -1:  Y#=X#(J)
  934. 740     FOR I= 7 TO 0 STEP-1
  935. 750 IF Y# > 2^I-1 THEN Z$=Z$+"1": Y#=Y#-2^I ELSE Z$=Z$+"0"
  936. 760     NEXT
  937. 770   NEXT:  N#=1:  Z$=RIGHT$(Z$,23)
  938. 780 FOR I=1 TO 23: N#= N# + VAL(MID$(Z$,I,1)) * .5^I:  NEXT
  939. 790 N$=STR$(N# * 2^(X#(4)-129)): PRINT #2, RIGHT$(N$,LEN(N$)-1); "!";
  940. 800 RETURN
  941. 815 '        30  NOT USED
  942. 816 '
  943. 819 '                                 31  =  DOUBLE PRECISION
  944. 820 FOR I=1 TO 8: GET 1: X#(I)=ASC(X$): NEXT:  Z$=""
  945. 830   FOR J=7 TO 1 STEP -1:  Y#=X#(J)
  946. 840    FOR I= 7 TO 0 STEP-1
  947. 850 IF Y# > 2^I-1 THEN Z$=Z$+"1": Y#=Y#-2^I ELSE Z$=Z$+"0"
  948. 860     NEXT
  949. 870   NEXT:  N#=1: Z$=RIGHT$(Z$,55)
  950. 880 FOR I=1 TO 55:  N#= N# + VAL(MID$(Z$,I,1)) * .5^I:  NEXT
  951. 890 N$=STR$(N# * 2^(X#(8)-129)):  PRINT #2, RIGHT$(N$,LEN(N$)-1); "#";
  952. 900 RETURN
  953. 905 '                        read from ' to end of line:
  954. 910 PRINT #2, "'";:  GET 1:
  955. 920 GET 1: IF ASC(X$) >0 THEN PRINT #2, X$;: GOTO 920
  956. 950 PRINT #2, "": RETURN
  957. 955 '
  958. 960 'tokens 129-244:
  959. 970 DATA
  960. END,FOR,NEXT,DATA,INPUT,DIM,READ,LET,GOTO,RUN,IF,RESTORE,GOSUB,RETURN
  961. 980 DATA
  962. REM,STOP,PRINT,CLEAR,LIST,NEW,ON,WAIT,DEF,POKE,CONT,NU,NU,OUT,LPRINT
  963. 990 DATA LLIST,NU,WIDTH,ELSE,TRON,TROFF,SWAP,ERASE,EDIT,ERROR,RESUME,DELETE
  964. 1000 DATA AUTO,RENUM,DEFSTR,DEFINT,DEFSNG,DEFDBL,LINE,WHILE,WEND,CALL,NU,NU,NU
  965. 1010 DATA  WRITE,OPTION,RANDOMIZE,OPEN,CLOSE,LOAD,MERGE,SAVE,COLOR,CLS,MOTOR
  966. 1020 DATA BSAVE,BLOAD,SOUND,BEEP,PSET,PRESET,SCREEN,KEY,LOCATE,NU,TO,THEN,TAB(
  967. 1030 DATA STEP,USR,FN,SPC,NOT,ERL,ERR,STRING$,USING,INSTR,"'",VARPTR,CSRLIN
  968. 1040 DATA POINT, OFF,INKEY$,NU,NU,NU,NU,NU,NU,NU,>,=,<,+,-,*,/,^,AND,OR,XOR,EQV
  969. 1050 DATA IMP,MOD
  970. 1060 'pre-token 253, tokens 129-134:
  971. 1070 DATA CVI,CVS,CVD,MKI$,MKS$,MKD$
  972. 1080 'pre-token 254, tokens 129-158:
  973. 1090 DATA FILES,FIELD,SYSTEM,NAME,LSET,RSET,KILL,PUT,GET,RESET,COMMON,CHAIN
  974. 1100 DATA DATE$,TIME$,PAINT,COM,CIRCLE,DRAW,PLAY,TIMER,ERDEV,IOCTL,CHDIR,MKDIR
  975. 1110 DATA RMDIR,SHELL,ENVIRON,VIEW,WINDOW,PMAP
  976. 1120 ' pre-token 255, tokens 129-165:
  977. 1130 DATA LEFT$,RIGHT$,MID$,SGN,INT,ABS,SQR,RND,SIN,LOG,EXP,COS,TAN,ATN,FRE
  978. 1140 DATA INP,POS,LEN,STR$,VAL,ASC,CHR$,PEEK,SPACE$,OCT$,HEX$,LPOS,CINT,CSNG
  979. 1150 DATA CDBL,FIX,PEN,STICK,STRIG,EOF,LOC,LOF
  980.  
  981. '
  982.  
  983.  
  984.                             OBJECT ORIENTED BASIC
  985.                           Possibility or Pipe Dream?
  986.  
  987.  
  988.                            an explorative article
  989.  
  990.  
  991.         TABLE OF CONTENTS
  992.  
  993.         1.0 Introduction
  994.         1.1 Key Terminology and Concepts
  995.         2.0 BASIC-Specific Considerations of Object Paradigm Implementation
  996.         2.1 Standardization of Terms in Object Oriented BASIC
  997.         2.2 An Introduction to Advanced Topics in OOP
  998.         3.0 Closing Notes
  999.  
  1000. =======================================================================
  1001.  
  1002.  
  1003. 1.0     Introduction
  1004.  
  1005.  
  1006. BASIC has evolved from the time-sharing "Beast of Dartmouth" into a
  1007. powerful, structured language fit for the programming needs of the
  1008. nineties.  Despite this evolution, however, major software compiler
  1009. developers have failed to introduce object oriented extensions into
  1010. the language.
  1011.  
  1012. This article will explore some possible extensions to modern
  1013. BASIC that would truly expand the language.  Since, because of its
  1014. nature, this article will use a speculative approach, the reader
  1015. should bear in mind that no particular implementation is being
  1016. suggested as the "best" way to bring object-orientation to
  1017. BASIC.  Moreover, some BASIC programmers may feel that certain low
  1018. level features such as in-line assembler and more diverse data
  1019. types should be introduced into the BASIC language before object-
  1020. orientation is even considered.  These readers should remember the
  1021. theoretical nature of this discussion, and leave all such
  1022. preferences out of the exploration at hand.
  1023.  
  1024.  
  1025. 1.1     Key Terminology and Concepts
  1026.  
  1027. First, I must define some key terms and concepts.  My use of the generic
  1028. term BASIC (Beginner's All-purpose Symbolic Instruction Code) will,
  1029. unless otherwise stated, refer to the Microsoft QuickBASIC v4.5 dialect
  1030. of BASIC, since this represents a widely accepted implemenation of
  1031. modern, structured BASIC.  The term OOP (Object Oriented Programming)
  1032. will be used to refer to those programming practices that rely on the
  1033. object paradigm.  Although the terminology differs from compiler to
  1034. compiler, the object oriented paradigm is considered by modern usage to
  1035. intrinsically encompass the following concepts, to be defined later:
  1036.  
  1037.     1.  Encapsulation
  1038.     2.  Inheritence
  1039.     3.  Polymorphism
  1040.     4.  Overloading
  1041.  
  1042. Therefore, when I say that a given concept is "object oriented" I
  1043. specifically mean that it involves the above four concepts.
  1044. Other important terms that cannot be ignored in any discussion of OOP,
  1045. due to their repeated use in the discussion of such are:
  1046.  
  1047.     5.  Class
  1048.     6.  Method (or Member Function)
  1049.     7.  Object (or Class Instance)
  1050.     8.  Information or Data Hiding
  1051.  
  1052. Not able to decide which term to define first, I will begin with a
  1053. general overview fo the underlying philosophy of OOP.
  1054.  
  1055. In classical structured programming, data and code are considered
  1056. separate entities.  Code manipulates data.  Data fuels code.  For
  1057. example, wanting to implement a graphics font engine, a classical
  1058. BASIC programmer might read a list of DATA statements into a globally
  1059. accessible array, and then have a series of globally accessible
  1060. SUBPROGRAMS manipulate those raster data on the screen in such a
  1061. way as to produce the desired visual effect.  The problem with this
  1062. approach is that both the data and the related code are equally
  1063. accessible, and they are only loosely cohesive.  Wanting to enhance
  1064. code written by a colleague, a second programmer will encounter
  1065. data structures that he should neither modify nor even poll, but
  1066. that may not always be possible.  Having modified an essential
  1067. data structure, the second programmer may introduce errors
  1068. into the whole underlying logic of the system.
  1069.  
  1070. For instance, suppose the original programmer had defined the
  1071. font data structure thus:
  1072.  
  1073.     TYPE FontDataType
  1074.         FontName AS STRING * 12
  1075.         FontPointSize AS INTEGER
  1076.         RasterDataPtr AS LONG
  1077.     END TYPE
  1078.  
  1079. Now, looking at this, Programmer Two decides that he can avoid a
  1080. FUNCTION call to funGetFontPointSize() by just reading the value of
  1081. Font.FontPointSize directly.  Programmer Two alters his code to
  1082. access this variable directly, and in doing so, avoids what he
  1083. considers costly calls to funGetFontPointSize().  He is
  1084. promoted to another department (presumably for having sped up the
  1085. code of his predecessor).  Enter Programmer Three.  Quite within
  1086. his bounds, he discovers that point size is never greater than 255,
  1087. and so redefines the whole structure to account for this, thereby
  1088. reducing overall memory consumption by one byte:
  1089.  
  1090.     TYPE FontDataType
  1091.         FontName AS STRING * 12
  1092.         FontPointSize AS STRING * 1
  1093.         RasterDataPtr AS LONG
  1094.     END TYPE
  1095.  
  1096. Of course, being conscientious, he modifies funGetFontPointSize()
  1097. to account for this innovation.  He compiles the program.  It crashes.
  1098. Why?  Because, this is now an illegal statement:
  1099.  
  1100.     Font.FontPointSize = 12
  1101.  
  1102. What to do?  He must use his search and replace to go through the
  1103. entire program and change all such instances to:
  1104.  
  1105.     Font.FontPointSize = CHR$(12)
  1106.  
  1107. Or, he can forget his alterations altogether.
  1108.  
  1109. In BASIC, there is no INFORMATION HIDING that would prevent such
  1110. problems from occuring.  Since FontPointSize is a public member
  1111. of FontDataType, Programmer Two was well within his rights to do
  1112. as he saw fit as far as accessing it.  Had the original programmer
  1113. had an object oriented BASIC, however, he could have prevented the
  1114. entire problem with little difficulty by making FontPointSize a
  1115. PRIVATE data member of the CLASS font.  This might have looked
  1116. similar to this:
  1117.  
  1118.     CLASS FontClass
  1119.         FontName AS PUBLIC STRING * 12
  1120.         FontPointSize AS PRIVATE INTEGER
  1121.         RasterDataPtr AS PRIVATE LONG
  1122.         funGetFontPointSize AS PUBLIC FUNCTION
  1123.         subSetFontPointSize AS PUBLIC SUB
  1124.     END CLASS
  1125.  
  1126.     DIM Font AS FontClass
  1127.  
  1128. [Please bear with the strange new syntax, since it will be covered
  1129. in more detail in section 2.0.]
  1130.  
  1131. Now, the only way to access Font.FontPointSize is indirectly.  This
  1132. would NOT work, since this data member is now PRIVATE:
  1133.  
  1134.     Font.FontPointSize = 12
  1135.  
  1136. This, then, would be the ONLY way to achieve such a thing:
  1137.  
  1138.     Font.subSetFontPointSize 12
  1139.  
  1140. In the above example, the item Font is what is called a CLASS INSTANCE.
  1141. That is to say, Font is "an instance of the class FontClass." This is
  1142. what is commonly called an OBJECT, and it is from this that we arrive at
  1143. the phrase "object oriented" programming.
  1144.  
  1145. Now, when Programmer Two comes along, he CANNOT pull off his stunt,
  1146. and he is not promoted to another department.  Programmer Three comes
  1147. along, and sees room for improvement and redefines the class thus:
  1148.  
  1149.     CLASS FontClass
  1150.         FontName AS PUBLIC STRING * 12
  1151.         FontPointSize AS PRIVATE STRING * 1
  1152.         RasterDataPtr AS PRIVATE LONG
  1153.         funGetFontPointSize AS PUBLIC FUNCTION
  1154.         subSetFontPointSize AS PUBLIC SUB
  1155.     END CLASS
  1156.  
  1157. Since all calls to change FontPointSize are through the centralized
  1158. subSetFontPointSize, Programmer Three just modifies that a bit, and
  1159. earns himself a nice raise in salary for shaving a byte off the
  1160. memory requirements of the structure.
  1161.  
  1162. Consider the above example.  The data are:
  1163.  
  1164.     1. FontName
  1165.     2. FontPointSize
  1166.  
  1167. The code portions (called MEMBER FUNCTIONS or METHODS, since they
  1168. are "methods of acting upon or accessing" the data) are:
  1169.  
  1170.     1. funGetFontPointSize
  1171.     2. subSetFontPointSize
  1172.  
  1173. Since it is unlikely that subSetFontPointSize will ever be needed for
  1174. anything other than the setting of FontPointSize, it makes sense to
  1175. bind the code to the data it works with.  This binding is called
  1176. ENCAPSULATION.
  1177.  
  1178. Having examined these more essential terms, there is the issue of
  1179. OVERLOADING.  Although not object oriented in the strictest sense,
  1180. it does aid in generalizing classes to an extent that they can
  1181. operate upon different types of data.
  1182.  
  1183. Consider the following:
  1184.  
  1185.     subQuickSort A%()
  1186.  
  1187. Now, in classical BASIC programming, if we wanted to sort anything
  1188. other than INTEGER arrays, we would have to write another SUBPROGRAM
  1189. and modify the algorithm to account for this new data type.  This
  1190. SUBPROGRAM would have to be named something other than subQuickSort.
  1191. For example:
  1192.  
  1193.     subQuickSortSTR A$()
  1194.  
  1195. might be used for STRING arrays, and
  1196.  
  1197.     subQuickSortLONG A&()
  1198.  
  1199. might be used for LONG INTEGER arrays.  And, of course, should a
  1200. programmer ever want to sort a user-defined TYPE array:
  1201.  
  1202.     subQuickSortUserTYPE UserArray()
  1203.  
  1204. would be the only way to do it.
  1205.  
  1206. But, consider the above.  All of these routines do the same thing.  It
  1207. seems a waste to have three names to do what amounts to the same thing:
  1208. sorting arrays.  The answer is to "overload" a SUBPROGRAM name with
  1209. three corresponding pieces of code.  Once subQuickSort is overloaded, it
  1210. can do tripple-time thus:
  1211.  
  1212.     subQuickSort A%()
  1213.     subQuickSort A$()
  1214.     subQuickSort UserArray()
  1215.  
  1216. Of course, each call invokes DIFFERENT CODE to do the actual sorting,
  1217. but this detail is handled by the compiler in a transparent fashion.
  1218. The programmer's only responsibility would be to provide the code for
  1219. each instance of subQuickSort, in the following manner:
  1220.  
  1221.     SUB subQuickSort (Array AS INTEGER)
  1222.         |
  1223.         |
  1224.         code to sort INTEGER arrays goes here
  1225.         |
  1226.     END SUB
  1227.  
  1228.     SUB subQuickSort (Array AS LONG)
  1229.         |
  1230.         |
  1231.         code to sort LONG INTEGER arrays goes here
  1232.         |
  1233.         |
  1234.     END SUB
  1235.  
  1236.     SUB subQuickSort (Array AS UserDefinedType)
  1237.         |
  1238.         |
  1239.         code to sort arrays of UserDefinedType goes here
  1240.         |
  1241.         |
  1242.     END SUB
  1243.  
  1244. Upon seeing the second instance of subQuickSort in the source listing,
  1245. the object oriented BASIC compiler would know that it is dealing with
  1246. an overloaded SUBPROGRAM.
  1247.  
  1248. Overloading is already done by BASIC compilers, but it is done at a
  1249. level not within the control of the programmer.  Consider:
  1250.  
  1251.     PRINT a
  1252.     PRINT a$
  1253.  
  1254. Each case of PRINT prints a different data type.  The PRINT statement,
  1255. we could say, then, is overloaded.  Also to consider is the overloading
  1256. of operators such as occurs already in BASIC:
  1257.  
  1258.     A$ = B$ + C$
  1259.     A% = B% + C%
  1260.  
  1261. The addition operator is serving two masters here.  In the first case,
  1262. it is being used to concactenate strings.  In the second, it is being
  1263. used to add two numbers.  The processes are internally dissimilar.
  1264. How, then, does the BASIC compiler contend with these cases?  The
  1265. addition operator is overloaded at an internal level.  If a programmer
  1266. using an object oriented BASIC were to step into the scene, however,
  1267. we very well might see this type of overloading of the addition and
  1268. assignment operators:
  1269.  
  1270.  
  1271.     OVERLOAD "+" FOR ArrayOne(), ArrayTwo()
  1272.         TotalElements = UBOUND(ArrayOne) + UBOUND(ArrayTwo)
  1273.         DIM ReturnArray(TotalElements)
  1274.         FOR i = 1 to UBOUND(ArrayOne)
  1275.             ReturnArray(i) = ArrayOne(i)
  1276.         NEXT i
  1277.         FOR q = i + 1 TO i + UBOUND(ArrayTwo)
  1278.             ReturnArray(q) = ArrayTwo(q-i)
  1279.         NEXT q
  1280.         REDIM ArrayOne(TotalElements)
  1281.  
  1282.         ' The following uses an overloaded assingment operator
  1283.         ' whose overload definition follows.
  1284.         ArrayOne() = ReturnArray()
  1285.     END OVERLOAD
  1286.  
  1287.     OVERLOAD "=" FOR ArrayOne(), ArrayTwo()
  1288.         FOR i = 1 TO UBOUND(ArrayOne)
  1289.             ArrayOne(i) = ArrayTwo(i)
  1290.         NEXT i
  1291.     END OVERLOAD
  1292.  
  1293. This bit of sophisticated operator overloading would allow the
  1294. programmers to add entire arrays to one another as follows:
  1295.  
  1296.     NewList() = ListOne() + ListTwo()
  1297.  
  1298. For some readers, all this may be a new concept in programming.  If
  1299. it seems hard to understand, please take time to reread this section
  1300. before continuing, since the next part of this discussion relies on
  1301. the reader's comprehension of all eight terms pertinent to the object
  1302. oriented programming paradigm, which are, again:
  1303.  
  1304.     1.  Encapsulation,
  1305.     2.  Inheritence,
  1306.     3.  Polymorphism,
  1307.     4.  Overloading,
  1308.     5.  Class,
  1309.     6.  Method (or Member Function),
  1310.     7.  Object (or Class Instance),
  1311.     8.  Information or Data Hiding.
  1312.  
  1313. [Polymorphism has been purposely avoided for the purposes of this
  1314. discussion, due to its rather esoteric nature.]
  1315.  
  1316. 2.0     BASIC-Specific Considerations of Object Paradigm Implementation
  1317.  
  1318. When considering whether BASIC in its present form could
  1319. be expanded to include object oriented extensions, we must first look
  1320. at what is already possible in standard BASIC.  For example, the
  1321. following code resembles inheritence, at least in part:
  1322.  
  1323.     TYPE ColorType
  1324.         R AS INTEGER
  1325.         G AS INTEGER
  1326.         B AS INTEGER
  1327.     END TYPE
  1328.  
  1329.     TYPE CoordinateType
  1330.         X AS INTEGER
  1331.         Y AS INTEGER
  1332.     END TYPE
  1333.  
  1334.     TYPE CircleType
  1335.         Point AS CoordinateType
  1336.         Color AS ColorType
  1337.         Radius AS INTEGER
  1338.     END TYPE
  1339.  
  1340. This is not classical inheritence, but the analogy suffices.  Looking
  1341. at the syntactical elements of the above code, we see that a similar
  1342. structure could easily be adopted for use with CLASS definitions:
  1343.  
  1344.     CLASS CircleClass
  1345.         Point AS CoordinateType
  1346.         Color AS ColorType
  1347.         Radius AS INTEGER
  1348.     END CLASS
  1349.  
  1350. A question arises, however.  The above definition of the CircleClass
  1351. CLASS is not executable code, but merely a definition template.  It
  1352. defines CircleClass, but does not assign a "class instance."  That is
  1353. to say, there are not yet any objects of CircleClass defined in the
  1354. program.  Consider this standard BASIC:
  1355.  
  1356.     TYPE AddressType
  1357.         Street AS STRING * 10
  1358.         City AS STRING * 32
  1359.         State AS STRING * 2
  1360.         ZIP AS STRING * 12
  1361.     END TYPE
  1362.  
  1363.     DIM Envelope AS AddressType
  1364.  
  1365. The DIM statement is used to create an instance of a variable
  1366. called Envelope that is of the user defined type AddressType.  It
  1367. makes perfect sense, then, that the DIM statement could be used
  1368. in this manner:
  1369.  
  1370.     CLASS CircleClass
  1371.         Point AS CoordinateType
  1372.         Color AS ColorType
  1373.         Radius AS INTEGER
  1374.     END CLASS
  1375.  
  1376.     DIM Orb AS CircleClass
  1377.  
  1378. (Remember, having DIM serve this double purpose is known as
  1379. overloading the DIM statement.)  This syntax serves our purposes
  1380. wonderfully, since it does not involve the introduction of completely
  1381. foreign operators and follows the present syntactical structure of
  1382. standard BASIC.
  1383.  
  1384. Another consideration in the creation of classes is the fact that
  1385. classes may contain both variables and methods in their definitions,
  1386. as shown in the introduction:
  1387.  
  1388.     CLASS FontClass
  1389.         FontName AS PUBLIC STRING * 12
  1390.         FontPointSize AS PRIVATE INTEGER
  1391.         RasterDataPtr AS PRIVATE LONG
  1392.         funGetFontPointSize AS PUBLIC FUNCTION
  1393.         subSetFontPointSize AS PUBLIC SUB
  1394.     END CLASS
  1395.  
  1396. This shows a suggested means of expressing both the scope and the
  1397. type of each part of the definition.  Note, however, that, although
  1398. subSetFontPointSize is defined in this template, there is, as yet,
  1399. no code attached to the definition.  It is said, in OOP parlance, that
  1400. the "the scope of the member function is unresolved."  The method is
  1401. prototyped, but that is all.  In C++, what is known as the "scope
  1402. resolution operator" is used to resolve a method, that is, assign
  1403. executable code to it.  This is done as follows:
  1404.  
  1405.  
  1406.     void FontClass::subSetFontPointSize (int PointSize)
  1407.     {
  1408.     |
  1409.     code to achieve this end goes here
  1410.     |
  1411.     }
  1412.  
  1413. Essentially, this translates into the English statement:
  1414.  
  1415.     "Define funGetFontPoint size of the class FontClass as follows...."
  1416.  
  1417. In an attempt to avoid convoluted syntactical introductions into the
  1418. BASIC language, what follows is a possible solution:
  1419.  
  1420.     SUB FontClass.subSetFontPointSize (PointSize AS INTEGER)
  1421.         |
  1422.         |
  1423.         code that assigns the point size goes here
  1424.         |
  1425.         |
  1426.     END SUB
  1427.  
  1428. Since the compiler would presumably recognize FontClass as being a
  1429. class from the earlier CLASS ... END CLASS block, this should suffice
  1430. as a means of resolving the scope of the method subSetFontPointSize,
  1431. while avoiding the introduction of :: as a new BASIC operator.
  1432.  
  1433. Next comes the issue of overloading both keywords and operators.  A
  1434. simple extension of BASIC would allow this to be sufficient in the
  1435. case of SUBPROGRAMS and FUNCTIONS:
  1436.  
  1437.  
  1438.     SUB subQuickSort (Array AS STRING)
  1439.         |
  1440.         |
  1441.     END SUB
  1442.  
  1443.     SUB subQuickSort (Array AS INTEGER)
  1444.         |
  1445.         |
  1446.     END SUB
  1447.  
  1448.  
  1449. The second SUB definition would imply overloading. This would be
  1450. prototyped at the beginning of the source listing thus:
  1451.  
  1452.     DECLARE SUB subQuickSort (Array AS STRING)
  1453.     DECLARE SUB subQuickSort (Array AS INTEGER)
  1454.  
  1455. Operators, however, are completely different in that BASIC has
  1456. no way of referring to them explicitly.  A proposed extension:
  1457.  
  1458.     OVERLOAD "=" FOR LeftArgument, RightArgument
  1459.         |
  1460.         |
  1461.         definition code goes here
  1462.         |
  1463.         |
  1464.         result returned in LeftArgument
  1465.         |
  1466.         |
  1467.     END OVERLOAD
  1468.  
  1469. Of course, the "=" could be any ASCII character or even multiple
  1470. ASCII characters.  This would allow the object oriented BASIC program
  1471. to do this, for example:
  1472.  
  1473.     OVERLOAD "**" FOR LeftArgument, RightArgument
  1474.  
  1475.         ' Some langauges use ** for raising to a power
  1476.         LeftArgument = LeftArgument ^ RightArgument
  1477.  
  1478.     END OVERLOAD
  1479.  
  1480. The following, however, would not be possible, since it would involve
  1481. late binding and interpreted evaluation at run-time:
  1482.  
  1483.     OVERLOAD Operator$ FOR LeftArgument, RightArgument
  1484.         SELECT CASE Operator$
  1485.             CASE "**"
  1486.                 LeftArgument = LeftArgument ^ RightArgument
  1487.             |
  1488.             |
  1489.             etc.
  1490.             |
  1491.             |
  1492.         END SELECT
  1493.     END OVERLOAD
  1494.  
  1495.  
  1496.  
  1497. 2.1     Standardization of Terms in Object Oriented BASIC
  1498.  
  1499. Before the discussion continues, perhaps it would be wise to step
  1500. aside to establish a set of standard terms.  Since certain
  1501. OOP concepts carry many different names (ie. "member function" is
  1502. also "method") a standard way of refering to any particular device
  1503. should be adopted.  But, really, this could become quite involved;
  1504. what is more appropriate, the term "method" or "member function?"
  1505. Perhaps, rather than debate too long and hard on the subject,
  1506. Microsoft's terminology as used for Visual Basic should be adopted:
  1507.  
  1508.     1.  OBJECT rather than "class instance"
  1509.     2.  METHOD rather than "member function"
  1510.     3.  PROPERTY rather than "member variable"
  1511.  
  1512. For terms not used by Visual Basic, I suggest the following use by
  1513. object oriented BASIC:
  1514.  
  1515.     1.  DATA HIDING rather than "information hiding"
  1516.     2.  METHOD DECLARATION rather than "scope resolution"
  1517.     3.  METHOD DECLARATOR rather than "scope resolution operator"
  1518.     4.  OBJECT BINDING rather than "encapsulation"
  1519.     5.  OVERLOADING remains unchanged
  1520.     6.  CLASS remains unchanged
  1521.  
  1522. I use these substitutes for the other terms because they have a
  1523. BASIC sound to them, whereas the other terms, like "scope resolution
  1524. operator" may sound odd to BASIC programmers.  DECLARATOR rings of
  1525. BASIC's DECLARE statement, thereby reducing the foreigness of the
  1526. term METHOD DECLARATOR.  (In case you have forgotten, the :: is
  1527. the scope resolution operator in C++, whereas the . is used in this
  1528. theoretical object oriented BASIC of ours.)
  1529.  
  1530. Using this terminology, we have this model:
  1531.  
  1532.       / CLASS VectorClass  ' This is a CLASS DECLARATION
  1533.       |     X AS PRIVATE INTEGER   ' This is a PROPERTY of VectorClass
  1534.  O B  |     Y AS PRIVATE INTEGER   ' As is this
  1535.  B I  |     '    ^^^^^^
  1536.  J N  |     ' Use of PRIVATE demonstrates DATA HIDING
  1537.  E D  |     ' Whereas use of PUBLIC demonstrates the oposite--\
  1538.  C I  |     '                                                 |
  1539.  T N  |     '                 /-------------------------------/
  1540.    G  |     '               VVVVVV
  1541.       |     subSetVector AS PUBLIC SUB ' This is a METHOD
  1542.       \ END CLASS
  1543.  
  1544.         '  This operator is the METHOD DECLARATOR in this context
  1545.         '              |
  1546.         '              V
  1547.     D / SUB VectorClass.subSetVector ( X AS INTEGER, Y AS INTEGER )
  1548.     E |
  1549.  M  C |
  1550.  E  L |
  1551.  T  A |
  1552.  H  R |
  1553.  O  A |
  1554.  D  T |
  1555.     I |
  1556.     O |
  1557.     N \ END SUB
  1558.  
  1559.  
  1560. 2.2     An Introduction to Advanced Topics in OOP
  1561.  
  1562. To this point, most fundemental concepts of the object oriented
  1563. paradigm have been examined.  The reader should have a concept of
  1564. class, object binding, method declaration, overloading, and
  1565. data hiding, and should also understand the essence of how these
  1566. object oriented extensions may be added to BASIC.
  1567.  
  1568. There are other considerations, however.  When an object is created,
  1569. for instance, how is it initialized?  That is to say, how are its
  1570. properties set to appropriate starting values?  A typical standard
  1571. BASIC program might accomplish this thus:
  1572.  
  1573.     CALL subFontInit()
  1574.  
  1575. This is fine, but remember that there can be more than one OBJECT of
  1576. the same CLASS as in this case:
  1577.  
  1578.     DIM Helvetica AS FontClass
  1579.     DIM Courier AS FontClass
  1580.     DIM TimesRoman AS FontClass
  1581.  
  1582. Now, to initialize the data for each of these, we must do something
  1583. like this:
  1584.  
  1585.     CALL subFontHelveticaInit
  1586.     CALL subFontCourierInit
  1587.     etc.
  1588.  
  1589. In C++, there is away around this that we can adopt for BASIC use.
  1590. In every class in C++ there is an implied "constructor."  This is
  1591. a new term.  Essentially, the constructor is a method within the
  1592. class definition that is executed whenever an object is created.
  1593. For an example of this, consider this method declaration:
  1594.  
  1595.     SUB FontClass.FontClass
  1596.         |
  1597.         |
  1598.         code to initialize object goes here
  1599.         |
  1600.         |
  1601.     END SUB
  1602.  
  1603. (Visual Basic programmers will recognize this as being analogous to
  1604. the Load_Form event.)  Note that the method declaration uses FontClass
  1605. twice.  This informs the compiler that it is dealing with the explicit
  1606. definition of a CONSTRUCTOR.
  1607.  
  1608. In the actual binding declaration of the class, this syntax is
  1609. suitable:
  1610.  
  1611.     CLASS FontType
  1612.         |
  1613.         etc.
  1614.         |
  1615.         FontType AS CONSTRUCTOR
  1616.         |
  1617.         etc.
  1618.         |
  1619.     END CLASS
  1620.  
  1621. The CONSTRUCTOR type then, signifies that this template will be
  1622. followed by a method declaration for a constructor.  Now, when the
  1623. programmer includes this code:
  1624.  
  1625.     DIM Helvetica AS FontType
  1626.  
  1627. The compiler will include appropriate initialization routines.
  1628.  
  1629. Another aspect of this, the "destructor," is exactly the same, except
  1630. that it operates after the object falls from scope.  (Visual Basic
  1631. programmers again will note the analagous use of the Form_Unload event.)
  1632. Destructors deinitialize data, cleaning up things when the program ends
  1633. execution, for instance.  In C++, a special operator is used to indicate
  1634. the deconstructor: ~FontClass.  This use of the tilde is foreign to
  1635. BASIC, however, so perhaps it would be better to introduce another
  1636. keyword rather than a new operator:
  1637.  
  1638.     CLASS FontType
  1639.         |
  1640.         etc.
  1641.         |
  1642.         FontType AS CONSTRUCTOR
  1643.         FontType AS DESTRUCTOR
  1644.         |
  1645.         etc.
  1646.         |
  1647.     END CLASS
  1648.  
  1649. Now, the method would simply be declared:
  1650.  
  1651.     SUB FontType.FontType DESTRUCTOR
  1652.         |
  1653.         |
  1654.         code to deinitialize data structures goes here
  1655.         |
  1656.         |
  1657.     END SUB
  1658.  
  1659. This is syntacally familiar to a BASIC programmer in another form:
  1660.  
  1661.     SUB subPrintToScreen (InText AS STRING) STATIC
  1662.         |
  1663.         |
  1664.     END SUB
  1665.  
  1666. The STATIC keyword modifies the nature of the SUBPROGRAM.  Consquently,
  1667. I have suggested the DESTRUCTOR keyword be used in a similar syntactical
  1668. fashion.
  1669.  
  1670. 3.0     Closing Notes
  1671.  
  1672. Indeed, BASIC has evolved from the time-sharing days of Dartmouth.
  1673. Despite this evolution, however, major software compiler developers
  1674. have failed to introduce object oriented extensions into the language.
  1675. Perhaps this article has introduced some new concepts to the reader,
  1676. perhaps not.  At the very least, it has explored some ways
  1677. an object oriented paradigm might be introduced successfully into
  1678. BASIC programming with as little pain possible.  Programmers tend to
  1679. maintain their old programming habbits despite the innovations that
  1680. come into their languages, and consequently, any major changes to
  1681. the way BASIC operates may prove to be obstacles rather than useful
  1682. tools.  I feel that my suggestions involve minimal relearning of the
  1683. syntax of BASIC, since they adopt the flavor of existing structures.
  1684. In the end, though, the question is not what is the better method
  1685. or terminology to use, really, but rather:
  1686.  
  1687.     "Object Oriented BASIC, possibility or pipedream?"
  1688.  
  1689. '
  1690.  
  1691.  From:  RICH GELDREICH            Sent: 07-15-93 02:59
  1692.    To:  ANDY THOMAS               Rcvd: -NO-
  1693.    Re:  (R)CALL INTERRUPT
  1694.  
  1695. > LA>DL> AL = AX AND &HFF
  1696. > LA>DL> AH = (AX \ &H100) AND &HFF
  1697. > LA>is very likely the most efficient way to do it.  I hope all
  1698. > LA>the people posting the wrong way to do it, (AH = AX\256) are
  1699. > LA>watching as well.
  1700. > Well, wrong is not quite the right word, as &H100 = 256 the two
  1701. > methods are equivalent. Tacking on the AND &HFF is redundant and will
  1702. > only change the value if something is wrong originally with the AX
  1703. > value. AX should be made of two bytes, and have a maximum value of
  1704. > &HFFFF only if AX is greater than this maximum value will the AND &HFF
  1705. > change anything. AH=AX\256 is a perfectly valid calculation and will
  1706. > work whenever the AX value is valid. If the AX value is invalid you
  1707. > don't want to be using it anyway! :> -Andy 
  1708.   
  1709.     Hmmm. Seems like everybody is wrong one way or another in this 
  1710. conversation... :-)
  1711.  
  1712.     First off, let's assume AX = &HFFFF. The correct result of shifting
  1713. AX right 8 places would be &HFF. The calculation for doing this above -
  1714. AH = (AX\&h100) AND &HFF, would *not* return &HFF, but &h0, because
  1715. (-1\&h100) = 0. (Remember QuickBASIC treats 16 bit words as signed, so
  1716. &HFFFF = -1.)
  1717.   
  1718.     In this context, AH=AX\256 is not a perfectly valid calculation. If
  1719. AX contains an unsigned integer, this calculation will not work
  1720. correctly all of the time. 
  1721.  
  1722.     This calculation does work for all values of AX:
  1723.  
  1724.     AL = AX AND &HFF
  1725.     AH = (CLNG(AX) AND &HFFFF&) \ &H100
  1726.  
  1727.     The "&" after &HFFFF is important.
  1728.  
  1729. '
  1730.  
  1731.  From:  CALVIN FRENCH             Sent: 07-16-93 00:00
  1732.    To:  ALL                       Rcvd: -NO-
  1733.    Re:  (1/6) C-SMENU.BAS V1.1
  1734.  
  1735. Here's a neat-o-roonie pulldown menu routine that I'm sure SOMEBODY 
  1736. will find good... nothing too spectacular, but useful anyways. 
  1737.  
  1738. 'C-SMENU1.BAS by Calvin French, 1993 
  1739. '------------------------------------------------------------- 
  1740. 'This code is entirely PD but if you use it please put my name 
  1741. 'in your program somewhere however you don't have to if you 
  1742. 'don't want to 
  1743. '------------------------------------------------------------- 
  1744.  
  1745. DEFINT A-Z 
  1746. '------ USED BY SUPERMENU 
  1747. DECLARE SUB PullDownMenu (y1, TopX1(), BoxX1(), BoxX2(),_ 
  1748.  LastOpt(), MenuDat$(), Fore, Back, SelectFore, SelectBack,_ 
  1749.  hilightfore, hilightback, KeyFore, ReturnX, ReturnY) 
  1750. DECLARE SUB DrawShadow (x1, y1, x2, y2) 
  1751. '---------------------------- 
  1752. DECLARE SUB DrawScreen ()             '<-- these ARE NOT used by SuperMenu
  1753. DECLARE SUB DrawBox (x1, y1, x2, y2)  '<---' 
  1754.  
  1755. CONST true = -1, false = 0 
  1756.  
  1757. 'To save space, I'll illustrate how the menu is set up, and then 
  1758. 'just read the rest of the menu data from DATA statments, ok? 
  1759.  
  1760. REDIM MenuDat$(5, 7) 
  1761. ' First element (8): This is the total number of headers that the 
  1762. '                    menu has 
  1763. ' Second element (7): This is the highest number of options, including 
  1764. '                    separator lines, that your menu has 
  1765. MenuDat$(1, 0) = " File " 
  1766. ' The 0th second element of each array is the header. 
  1767. MenuDat$(1, 1) = " ^New Program" 
  1768. ' The carat (^) is used to illustrate a hotkey. 
  1769. ' NOTE: YOU MUST SPECIFY ONE HOTKEY PER MENU OPTION! 
  1770. MenuDat$(1, 2) = " ^Open Program" 
  1771. MenuDat$(1, 3) = " Save ^As" 
  1772. MenuDat$(1, 4) = "^:" 
  1773. ' The "^:" means put a line separator in there. 
  1774. MenuDat$(1, 5) = " ^Print" 
  1775. MenuDat$(1, 6) = "^:" 
  1776. MenuDat$(1, 7) = " E^xit" 
  1777. TopX1(1) = 3 
  1778. ' This is the X location where the header (e.g., " File ") is to appear
  1779.  
  1780. BoxX1(1) = 2 
  1781. ' This is the X location where the left hand side of the box is to be 
  1782. BoxX2(1) = 20 
  1783. ' This is the X location where the right hand side of the box is to be 
  1784. LastOpt(1) = 7 
  1785. ' This is the number of options in the particular menu 
  1786.  
  1787. 'Now, to save space, I'll just read the rest of them... 
  1788. FOR n = 2 TO 5 
  1789.   READ LastOpt(n) 
  1790.   READ TopX1(n) 
  1791.   READ BoxX1(n) 
  1792.   READ BoxX2(n) 
  1793.   FOR x = 0 TO LastOpt(n) 
  1794.     READ MenuDat$(n, x) 
  1795.   NEXT x 
  1796. NEXT n 
  1797. '-------------------------------- 
  1798. 'Effiecient way of doing toggles: 
  1799. DIM ToggleChar$(-1 TO 0)      'true false 
  1800. ToggleChar$(-1) = CHR$(254) 
  1801. ToggleChar$(0) = " " 
  1802.  
  1803. Toggle1 = false       'off 
  1804.  
  1805. Fore = 0              'color for menu 
  1806. Back = 7 
  1807. SelectFore = 15       'color for topic bar. QB uses 7, 0 but i like 15, 1
  1808. SelectBack = 1 
  1809. hilightfore = 7       'color for menu bar inside menu 
  1810. hilightback = 0 
  1811. KeyFore = 15          'hilight color for hotkeys 
  1812.  
  1813. y1 = 1                'the Y loc you want the menu to appear on 
  1814.  
  1815. COLOR 15, 1 
  1816. PRINT STRING$(2000, 176); 
  1817.  
  1818. DrawScreen 
  1819.  
  1820. DO 
  1821.  
  1822.   MenuDat$(4, 4) = ToggleChar$(Toggle1) + "^Toggle 1" 
  1823.  
  1824.   PullDownMenu 1, TopX1(), BoxX1(), BoxX2(), LastOpt(),_ 
  1825.  MenuDat$(), Fore, Back, SelectFore, SelectBack, hilightfore,_ 
  1826.  hilightback, KeyFore, ReturnX, ReturnY 
  1827.   'ReturnX is the returned X value 
  1828.   'ReturnY is the returned Y value 
  1829.   ' 
  1830.   'If you set it up to reroute ReturnX/ReturnY, you will be able to keep
  1831.   'the menu bar on the option the user last selected... nice... 
  1832.  
  1833.   IF ReturnX = 4 AND ReturnY = 4 THEN Toggle1 = NOT Toggle1 
  1834.  
  1835. LOOP UNTIL ReturnX = 1 AND ReturnY = 7 
  1836.  
  1837. COLOR 7, 0 
  1838. CLS 
  1839. PRINT "Calvin French, 1993" 
  1840. PRINT "Seeya!" 
  1841.  
  1842. END 
  1843.  
  1844.  
  1845. 'Menu data. I did it like this to save space. You don't have to do it like
  1846. ' this (read it into the array) 
  1847. PulldownMenuData: 
  1848. DATA 3,9,8,32," Edit "," Cu^t         Shift+DEL"," ^Copy         Ctrl+INS"," ^Paste       Shift+INS"
  1849. DATA 3,15,14,39," View "," ^SUBs...            F2"," O^utput Screen      F4"," Included ^Lines"
  1850. DATA 4,21,20,48," Search "," ^Find..."," ^Change...","^:","^Toggle 1"
  1851. DATA 4,72,51,78," Help "," ^Index"," ^Contents"," ^Topic:                F1"," ^Help on Help    Shift+F1"
  1852.  
  1853. SUB DrawBox (x1, y1, x2, y2) 
  1854.  
  1855. LOCATE y1, x1 
  1856. PRINT CHR$(218); STRING$(x2 - x1 - 2, 196); CHR$(191); 
  1857.  
  1858. FOR n = y1 + 1 TO y2 - 1 
  1859.   LOCATE n, x1 
  1860.   PRINT CHR$(179); SPACE$(x2 - x1 - 2); CHR$(179); 
  1861. NEXT n 
  1862.  
  1863. LOCATE y2, x1 
  1864. PRINT CHR$(192); STRING$(x2 - x1 - 2, 196); CHR$(217); 
  1865.  
  1866.  
  1867. DrawShadow x1, y1, x2, y2 
  1868.  
  1869. END SUB 
  1870.  
  1871. SUB DrawScreen 
  1872.  
  1873. COLOR 15, 0 
  1874. DrawBox 9, 6, 70, 19 
  1875.  
  1876. COLOR 15, 0 
  1877. LOCATE 7, 10 
  1878. PRINT " C-SMENU1.1, By Calvin French, May (sometime) 1993         "
  1879. COLOR 14 
  1880. LOCATE 8, 10 
  1881. PRINT "-----------------------------------------------------------"
  1882. COLOR 13 
  1883. LOCATE 9, 10 
  1884. PRINT " This is a small and power packed versitile pulldown menu  "
  1885. LOCATE 10, 10 
  1886. PRINT " routine I put together awhile ago. I've added shadows in  "
  1887. LOCATE 11, 10 
  1888. PRINT " This version, 1.1. 1.0 diddn't have shadows. Okay if you  "
  1889. LOCATE 12, 10 
  1890. PRINT " want to use these routines, you may, but please put my    "
  1891. LOCATE 13, 10 
  1892. PRINT " name on it below your copyright or something. It's a very "
  1893. LOCATE 14, 10 
  1894. PRINT " fast routine, and lacks only the ALT+Letter routine to    "
  1895. LOCATE 15, 10 
  1896. PRINT " access the menus, which is your job. It is all-QB, so you "
  1897. LOCATE 16, 10 
  1898. PRINT " who don't like to use add-on libs should like this one!   "
  1899. COLOR 14 
  1900. LOCATE 17, 10 
  1901. PRINT " Have fun,                                                 "
  1902. COLOR 15 
  1903. LOCATE 18, 10 
  1904. PRINT " - Calvin -                                                "
  1905.  
  1906. END SUB 
  1907.  
  1908. SUB DrawShadow (x1, y1, x2, y2) 
  1909.  
  1910. DEF SEG = &HB800 
  1911.  
  1912. YMem = y2 * 160 
  1913. XMem = (x2 * 2) + 1 
  1914.  
  1915. COLOR 8, 0 
  1916.  
  1917. FOR n = x1 + 2 TO x2 + 1 
  1918.   MemLoc = YMem + n * 2 - 1 
  1919.   POKE MemLoc, 8 
  1920. NEXT n 
  1921.  
  1922. FOR n = y1 + 1 TO y2 + 1 
  1923.   MemLoc = ((n - 1) * 160) + XMem - 2 
  1924.   POKE MemLoc, 8 
  1925.   POKE MemLoc + 2, 8 
  1926. NEXT n 
  1927.  
  1928. DEF SEG 
  1929.  
  1930. END SUB 
  1931.  
  1932. SUB PullDownMenu (y1, TopX1(), BoxX1(), BoxX2(), LastOpt(),_ 
  1933.  MenuDat$(), Fore, Back, SelectFore, SelectBack, hilightfore,_ 
  1934.  hilightback, KeyFore, ReturnX, ReturnY) 
  1935.  
  1936. DIM CurrY(UBOUND(MenuDat$, 1)) 
  1937.  
  1938. OldX = 1 
  1939. CurrX = 1 
  1940. PulledDown = false 
  1941.  
  1942.  
  1943. DIM HotKey(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2)) 
  1944. DIM leftside$(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2)) 
  1945. DIM rightside$(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2)) 
  1946.  
  1947. COLOR Fore, Back 
  1948. LOCATE y1, 1 
  1949. PRINT SPACE$(80); 
  1950.  
  1951. GOSUB PrintTopBar 
  1952. GOSUB SaveScreen2 
  1953.  
  1954. FOR x = 1 TO UBOUND(MenuDat$, 1) 
  1955.   FOR y = 1 TO LastOpt(x) 
  1956.     HotKeyLoc = INSTR(MenuDat$(x, y), "^") 
  1957.     leftside$(x, y) = MID$(MenuDat$(x, y), 1, HotKeyLoc - 1) 
  1958.     HotKey(x, y) = ASC(MID$(MenuDat$(x, y), HotKeyLoc + 1)) 
  1959.     rightside$(x, y) = MID$(MenuDat$(x, y), HotKeyLoc + 2) 
  1960.   NEXT y 
  1961. NEXT x 
  1962.  
  1963. IF ReturnX > 0 OR ReturnY > 0 THEN 
  1964.   FOR n = 1 TO UBOUND(MenuDat$, 1) 
  1965.     CurrY(n) = 1 
  1966.   NEXT n 
  1967.   CurrX = ReturnX 
  1968.   CurrY(CurrX) = ReturnY 
  1969.   PulledDown = true 
  1970.   GOSUB PrintCurrentMenu 
  1971. END IF 
  1972.  
  1973. GOSUB PrintCurrentTop 
  1974.  
  1975. DO 
  1976.  
  1977.   GOSUB PrintMenu 
  1978.  
  1979.   DO 
  1980.     key$ = INKEY$ 
  1981.   LOOP UNTIL LEN(key$) 
  1982.  
  1983.   KeyCode = ASC(RIGHT$(key$, 1)) 
  1984.  
  1985.   SELECT CASE KeyCode 
  1986.     CASE 75               'left 
  1987.       CurrX = CurrX - 1 
  1988.       IF CurrX < 1 THEN CurrX = UBOUND(MenuDat$, 1) 
  1989.     CASE 77               'right 
  1990.       CurrX = CurrX + 1 
  1991.       IF CurrX > UBOUND(MenuDat$, 1) THEN CurrX = 1 
  1992.     CASE 72               'up 
  1993.       IF PulledDown = true THEN 
  1994.         CurrY(CurrX) = CurrY(CurrX) - 1 
  1995.         IF MenuDat$(CurrX, CurrY(CurrX)) = "^:" THEN CurrY(CurrX) = CurrY(CurrX) - 1
  1996.         IF CurrY(CurrX) < 1 THEN CurrY(CurrX) = LastOpt(CurrX) 
  1997.         GOSUB PrintCurrentMenu 
  1998.       END IF 
  1999.     CASE 80               'down 
  2000.       IF PulledDown = false THEN 
  2001.         FOR n = 1 TO UBOUND(MenuDat$, 1) 
  2002.           CurrY(n) = 1 
  2003.         NEXT n 
  2004.         PulledDown = true 
  2005.       ELSE 
  2006.         CurrY(CurrX) = CurrY(CurrX) + 1 
  2007.         IF CurrY(CurrX) > LastOpt(CurrX) THEN CurrY(CurrX) = 1 
  2008.         IF MenuDat$(CurrX, CurrY(CurrX)) = "^:" THEN CurrY(CurrX) = CurrY(CurrX) + 1
  2009.         IF CurrY(CurrX) > LastOpt(CurrX) THEN CurrY(CurrX) = 1 
  2010.       END IF 
  2011.       GOSUB PrintCurrentMenu 
  2012.     CASE 13 
  2013.       IF PulledDown = true THEN 
  2014.         GOSUB RestoreScreen2 
  2015.         ReturnX = CurrX 
  2016.         ReturnY = CurrY(CurrX) 
  2017.         EXIT SUB 
  2018.       ELSE 
  2019.         PulledDown = true 
  2020.         FOR n = 1 TO UBOUND(MenuDat$, 1) 
  2021.           CurrY(n) = 1 
  2022.         NEXT n 
  2023.         GOSUB PrintCurrentMenu 
  2024.       END IF 
  2025.     CASE ELSE 
  2026.  
  2027.       KeyCode = KeyCode OR 32 
  2028.       Search = HotKey(CurrX, CurrY(CurrX)) OR 32 
  2029.       found = false 
  2030.  
  2031.       FOR n = CurrY(CurrX) + 1 TO LastOpt(CurrX) 
  2032.         Search = HotKey(CurrX, n) OR 32 
  2033.         IF Search = KeyCode THEN 
  2034.           CurrY(CurrX) = n 
  2035.           found = true
  2036.  
  2037.           EXIT FOR 
  2038.         END IF 
  2039.       NEXT 
  2040.  
  2041.       FOR n = 1 TO CurrY(CurrX) 
  2042.         Search = HotKey(CurrX, n) OR 32 
  2043.         IF Search = KeyCode THEN 
  2044.           IF found = false THEN 
  2045.             CurrY(CurrX) = n 
  2046.             found = true 
  2047.           END IF 
  2048.           EXIT FOR 
  2049.         END IF 
  2050.       NEXT n 
  2051.  
  2052.       IF found = true THEN 
  2053.         GOSUB PrintCurrentMenu 
  2054.         ReturnX = CurrX 
  2055.         ReturnY = CurrY(CurrX) 
  2056.         GOSUB RestoreScreen2 
  2057.         EXIT SUB 
  2058.       END IF 
  2059.  
  2060.   END SELECT
  2061.  
  2062.  
  2063. LOOP 
  2064.  
  2065. EXIT SUB 
  2066.  
  2067. PrintTopBar: 
  2068. FOR n = 1 TO UBOUND(MenuDat$, 1) 
  2069.   LOCATE y1, TopX1(n) 
  2070.   PRINT MenuDat$(n, 0); 
  2071. NEXT n 
  2072. RETURN 
  2073.  
  2074. PrintCurrentTop: 
  2075. LOCATE y1, TopX1(CurrX) 
  2076. COLOR SelectFore, SelectBack 
  2077. PRINT MenuDat$(CurrX, 0); 
  2078. RETURN 
  2079.  
  2080. SaveScreen2: 
  2081. PCOPY 0, 2    'PCOPY is very fast, and well suited for this task 
  2082. RETURN 
  2083.  
  2084. RestoreScreen2: 
  2085. PCOPY 2, 0    'PCOPY is very fast, and well suited for this task 
  2086. RETURN 
  2087.  
  2088. PrintMenu: 
  2089. IF CurrX <> OldX THEN 
  2090.   GOSUB RestoreScreen2 
  2091.   GOSUB PrintCurrentTop 
  2092.   IF PulledDown = true THEN 
  2093.     GOSUB PrintCurrentMenu 
  2094.   END IF 
  2095. END IF 
  2096.  
  2097. OldX = CurrX 
  2098.  
  2099. RETURN 
  2100.  
  2101. PrintCurrentMenu: 
  2102.  
  2103. COLOR Fore, Back 
  2104. LOCATE y1 + 1, BoxX1(CurrX) 
  2105.  
  2106. PRINT CHR$(218); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(191)
  2107.  
  2108. FOR n = 1 TO LastOpt(CurrX) 
  2109.   LOCATE y1 + n + 1, BoxX1(CurrX) 
  2110.   PRINT CHR$(179); 
  2111.   IF MenuDat$(CurrX, n) <> "^:" THEN 
  2112.     IF n <> CurrY(CurrX) THEN 
  2113.       COLOR Fore, Back 
  2114.       PRINT leftside$(CurrX, n); 
  2115.       COLOR KeyFore, Back 
  2116.       PRINT CHR$(HotKey(CurrX, n)); 
  2117.       COLOR Fore, Back 
  2118.       PRINT rightside$(CurrX, n); 
  2119.       PRINT SPACE$(BoxX2(CurrX) - BoxX1(CurrX) - LEN(MenuDat$(CurrX, n)));
  2120.     ELSE 
  2121.       COLOR hilightfore, hilightback 
  2122.       PRINT leftside$(CurrX, n); 
  2123.       COLOR KeyFore, hilightback 
  2124.       PRINT CHR$(HotKey(CurrX, n)); 
  2125.       COLOR hilightfore, hilightback 
  2126.       PRINT rightside$(CurrX, n); 
  2127.       PRINT SPACE$(BoxX2(CurrX) - BoxX1(CurrX) - LEN(MenuDat$(CurrX, n)));
  2128.     END IF 
  2129.     COLOR Fore, Back 
  2130.     PRINT CHR$(179); 
  2131.   ELSE 
  2132.     COLOR Fore, Back 
  2133.     LOCATE y1 + n + 1, BoxX1(CurrX) 
  2134.     PRINT CHR$(195); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(180);
  2135.   END IF 
  2136. NEXT n 
  2137.  
  2138. LOCATE y1 + LastOpt(CurrX) + 2, BoxX1(CurrX) 
  2139. PRINT CHR$(192); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(217)
  2140.  
  2141. DrawShadow BoxX1(CurrX), y1 + 1, BoxX2(CurrX) + 1, LastOpt(CurrX) + 3
  2142.  
  2143. RETURN 
  2144.  
  2145. END SUB 
  2146.  
  2147. '
  2148.  
  2149. ' From:  IAN REMMLER               Sent: 07-17-93 21:57
  2150. '   To:  ALL                       Rcvd: -NO-
  2151. '   Re:  3D STRANGE ATTRACTOR DEMO
  2152. '
  2153. 'Hey everybody!
  2154. 'Here's a 3D attractor demo that lets you rotate the attractor on
  2155. 'the X, Y, and Z axes then view it. (like on FractInt) If you have
  2156. 'a mouse you can use it to zoom in. I didn't spend time on putting
  2157. 'in a keyboard equivalent, so if you don't have a mouse you can make
  2158. 'a routine for the keyboard, or change the WINDOW statement manually.
  2159. 'Have fun with it, and feel free to hack it to pieces.
  2160.  
  2161. DECLARE SUB Crsr (x!)
  2162. DECLARE SUB zoom ()
  2163. DECLARE FUNCTION MPos (Coord)
  2164. DECLARE FUNCTION MBttn ()
  2165.  
  2166. '$INCLUDE: 'qb.bi'
  2167. DIM SHARED Regs AS RegTypeX
  2168. DIM sine(359) AS SINGLE, cosine(359) AS SINGLE
  2169.  
  2170. CONST pi = 3.1415926535#
  2171.  
  2172. dist = 100 'This is the distance from the screen used in the
  2173.  'perspective formula.
  2174.  
  2175. AngleX = 0 '\
  2176. AngleY = 0 ' > angles of rotation
  2177. AngleZ = 0 '/
  2178.  
  2179. SCREEN 9
  2180. PRINT "Creating SIN/COS Tables..."
  2181. PRINT : PRINT "Press F1 any time to goto Main Menu."
  2182. PRINT "Press F2 any time to exit program."
  2183. PRINT : PRINT "If you have a mouse, you can use the LMB to"
  2184. PRINT "zoom in on an attractor,or the RMB to exit."
  2185. LOCATE 24, 1: PRINT "Strange Attractor Demo - By: Ian Remmler.";
  2186. twirl$ = "\-/" + CHR$(179)
  2187.  
  2188. FOR t = 0 TO 359 'create sine/cosine tables
  2189. LOCATE 1, 28: PRINT MID$(twirl$, t MOD 4 + 1, 1)
  2190. sine(t) = SIN(t * (pi / 180))
  2191. cosine(t) = COS(t * (pi / 180))
  2192. NEXT
  2193.  
  2194. KEY(1) ON            '\
  2195. KEY(2) ON            ' \ sets up event trapping on F1 & F2
  2196. ON KEY(1) GOSUB Main ' /
  2197. ON KEY(2) GOSUB Leave'/
  2198. GOSUB WhichOne
  2199.  
  2200. Lorenz: 'Converted from the FractInt documentation.
  2201. WINDOW (-32, -0)-(32, 35)
  2202. dt = .05
  2203. a = 3
  2204. b = 15
  2205. c = 1
  2206.  
  2207. Lstart:
  2208. x = 1
  2209. y = 1
  2210. z = 1
  2211.  
  2212. CLS
  2213. GOSUB Translate
  2214. PSET (xxx, yyy)
  2215. Crsr 1
  2216. DO
  2217. xx = x: yy = y: zz = z
  2218. x = xx + (-a * xx * dt) + (a * yy * dt)
  2219. y = yy + (b * xx * dt) - (yy * dt) - (zz * xx * dt)
  2220. z = zz + (-c * zz * dt) + (xx * yy * dt)
  2221. GOSUB Translate
  2222. Crsr 0: LINE -(xxx, yyy): Crsr 1
  2223. IF MBttn = 1 THEN zoom: GOTO Lstart
  2224. LOOP UNTIL MBttn = 2
  2225. Crsr 0
  2226. GOSUB Leave
  2227.  
  2228. Rossler: 'Also from FractInt docs.
  2229. WINDOW (-64, -35)-(64, 35)
  2230. dt = .05
  2231. a = .2
  2232. b = .2
  2233. c = 5.7
  2234.  
  2235. Rstart:
  2236. x = 1
  2237. y = 1
  2238. z = 1
  2239.  
  2240. CLS
  2241. GOSUB Translate
  2242. PSET (xxx, yyy)
  2243. Crsr 1
  2244. DO
  2245. xx = x: yy = y: zz = z
  2246. x = xx - yy * dt - zz * dt
  2247. y = yy + xx * dt + a * yy * dt
  2248. z = zz + b * dt + xx * zz * dt - c * zz * dt
  2249. GOSUB Translate
  2250. Crsr 0: LINE -(xxx, yyy): Crsr 1
  2251. IF MBttn = 1 THEN zoom: GOTO Rstart
  2252. LOOP UNTIL MBttn = 2
  2253. Crsr 0
  2254. GOSUB Leave
  2255.  
  2256. Mutant: 'Wrote this one all by myself! Pretty spiffy, huh?
  2257. WINDOW (-32, -17.5)-(32, 17.5)
  2258. dt = .02
  2259. a = 8
  2260. b = 10
  2261. c = 10
  2262.  
  2263. Mstart:
  2264. x = 1
  2265. y = 1
  2266. z = 1
  2267.  
  2268. CLS
  2269. GOSUB Translate
  2270. PSET (xxx, yyy)
  2271. Crsr 1
  2272. DO
  2273. xx = x: yy = y: zz = z
  2274. x = xx - (a * zz * dt) + (-a * yy * dt)
  2275. y = yy + (b * xx * dt) - (yy * dt) - (zz * xx * dt)
  2276. z = zz + (-c * zz * dt) - (xx * yy * dt)
  2277. GOSUB Translate
  2278. Crsr 0: LINE -(xxx, yyy): Crsr 1
  2279. IF MBttn = 1 THEN zoom: GOTO Mstart
  2280. LOOP UNTIL MBttn = 2
  2281. Crsr 0
  2282. GOSUB Leave
  2283.  
  2284. Translate: 'converts x,y,z coords. to x,y so they can be put on
  2285.  'the screen. We use the basic rotation formula on the
  2286.  'X axis, then Y, then Z.
  2287. za = z * sine(AngleX) - y * cosine(AngleX) '\ X rotation
  2288. ya = z * cosine(AngleX) + y * sine(AngleX) '/
  2289. za = za * sine(AngleY) - x * cosine(AngleY) '\ Y rotation
  2290. xa = za * cosine(AngleY) + x * sine(AngleY) '/
  2291. xa = ya * sine(AngleZ) - xa * cosine(AngleZ) '\ Z rotation
  2292. ya = ya * cosine(AngleZ) + xa * sine(AngleZ) '/
  2293. xxx = xa * (dist / (dist + za))
  2294. yyy = ya * (dist / (dist + za))
  2295. RETURN
  2296.  
  2297. Main:
  2298. KEY(1) ON
  2299. ON KEY(1) GOSUB Main
  2300. CLS : PRINT "Main Menu!"
  2301. PRINT "1. Select an Attractor."
  2302. PRINT "2. Change Parameters."
  2303. PRINT "3. Change Angles of Rotation."
  2304. PRINT
  2305. DO: q = VAL(INKEY$)
  2306. LOOP WHILE q = 0
  2307. ON q GOSUB WhichOne, Params, Angles
  2308. CLS
  2309. RETURN
  2310.  
  2311. WhichOne:
  2312.  
  2313. CLS : PRINT "Select an Attractor!"
  2314. PRINT : PRINT "1. Lorenz"
  2315. PRINT "2. Rossler"
  2316. PRINT "3. Mutant"
  2317. PRINT
  2318. DO: q = VAL(INKEY$)
  2319. LOOP WHILE q = 0
  2320. ON q GOSUB Lorenz, Rossler, Mutant
  2321.  
  2322. Params:
  2323. PRINT "Input New Parameters!"
  2324. PRINT
  2325. PRINT "A= "; a; " "; : INPUT a
  2326. PRINT "B= "; b; " "; : INPUT b
  2327. PRINT "C= "; c; " "; : INPUT c
  2328. PRINT "DT= "; dt; " "; : INPUT dt
  2329. CLS
  2330. RETURN
  2331.  
  2332. Angles:
  2333. PRINT "Change Angles of Rotation!"
  2334. PRINT
  2335. PRINT "X= "; AngleX; " "; : INPUT AngleX
  2336. PRINT "Y= "; AngleY; " "; : INPUT AngleY
  2337. PRINT "Z= "; AngleZ; " "; : INPUT AngleZ
  2338. PRINT "Perspective Distance= "; dist; " "; : INPUT dist
  2339. CLS
  2340. RETURN
  2341.  
  2342. Leave:
  2343. END
  2344.  
  2345. SUB Crsr (x) 'this sub turns the pointer on and off (Crsr 0=off)
  2346. SELECT CASE x                                     '(Crsr 1=on)
  2347. CASE 0
  2348. Regs.ax = 2
  2349. CASE 1
  2350. Regs.ax = 1
  2351. END SELECT
  2352. CALL INTERRUPTX(&H33, Regs, Regs)
  2353. END SUB
  2354.  
  2355. FUNCTION MBttn 'returns which buttons are currently down.
  2356. Regs.ax = 3
  2357. CALL INTERRUPTX(&H33, Regs, Regs)
  2358. MBttn = Regs.bx
  2359. END FUNCTION
  2360.  
  2361. FUNCTION MPos (Coord) 'returns the x,y coords. of the mouse
  2362. Regs.ax = 3
  2363. CALL INTERRUPTX(&H33, Regs, Regs)
  2364. SELECT CASE Coord
  2365. CASE 0
  2366. MPos = Regs.cx
  2367. CASE 1
  2368. MPos = Regs.dx
  2369. END SELECT
  2370. END FUNCTION
  2371.  
  2372. SUB zoom 'zooms in on the attractor using the mouse.
  2373. Crsr 0
  2374. PCOPY 0, 1
  2375. Crsr 1
  2376. x1 = PMAP(MPos(0), 2)
  2377. y1 = PMAP(MPos(1), 3)
  2378. DO
  2379. Crsr 0
  2380. PCOPY 1, 0
  2381. x2 = PMAP(MPos(0), 2)
  2382. y2 = PMAP(MPos(1), 3)
  2383. Crsr 1
  2384. LINE (x1, y1)-(x2, y2), , B
  2385. WHILE PMAP(MPos(0), 2) = x2 AND PMAP(MPos(1), 3) = y2: WEND
  2386. LOOP WHILE MBttn = 1
  2387. WINDOW (x1, y1)-(x2, y2)
  2388. Crsr 0
  2389. END SUB
  2390.  
  2391.  
  2392. '
  2393.  
  2394.  
  2395.  From:  MARK BUTLER               Sent: 07-16-93 20:32
  2396.    To:  ALL                       Rcvd: -NO-
  2397.    Re:  QWINDOWS  1/4
  2398.  
  2399.  Hello ALL, a couple years ago I posted a collection of QB windowing 
  2400.  routines. Many folks have come and gone from this conference since 
  2401.  then so I thought maybe I'd post them again for those who never saw 
  2402.  them. For those well familiar with these routines a new one has been 
  2403.  added -"UnScroll" which is a variation of the "Expand" routine that 
  2404.  expands to it's full height vertically before it begins to expand 
  2405.  horizontally. 
  2406. ==========================8< Cut Here 8<=============================
  2407.  ' Shadowed window routines by Mark H Butler placed into the public domain
  2408.  ' on February 28, 1992 and revised July 16 1993. I would appreciate any
  2409.  ' feedback on these routines and if you improve on them I'd kinda like to
  2410.  ' know what you did so I can benefit by the improvements to.  If that's a
  2411.  ' deal then enjoy the routines, gratis =MHB=
  2412.  
  2413.  DEFINT A-Z
  2414.  DECLARE SUB Drawbox (UpRow%, LtCol%, LoRow%, RtCol%)
  2415.  DECLARE SUB Shadow (UpRow%, LtCol%, LoRow%, RtCol%)
  2416.  DECLARE SUB Explode (UpRow%, LtCol%, LoRow%, RtCol%)
  2417.  DECLARE SUB Expand (UpRow%, LtCol%, LoRow%, RtCol%)
  2418.  ' UnScroll is the routine that is new to this collection.
  2419.  DECLARE SUB UnScroll (UpRow%, LtCol%, LoRow%, RtCol%)
  2420.  DECLARE SUB ScreenClear (LineColor%)
  2421.  DECLARE SUB Pause (ticks%)
  2422.  
  2423.  ' These first lines of code are included to demo the
  2424.  ' Exploding, expanding and unscrolling window routines.
  2425.  ' We'll fill the sceen with a bunch of crap so our windows
  2426.  ' will have a backdrop you can see their Shadows against.
  2427.  
  2428.     LOCATE , , 0
  2429.     COLOR 14, 1
  2430.     CLS
  2431.     FOR I = 1 TO 13
  2432.         FOR ch = 33 TO 178
  2433.             PRINT CHR$(ch);
  2434.         NEXT ch
  2435.     NEXT I
  2436.  
  2437.     COLOR 4, 7
  2438.     Explode 5, 15, 15, 65
  2439.  
  2440.     COLOR 0
  2441.     LOCATE 9, 27
  2442.     PRINT "This 'Exploding' window was"
  2443.     LOCATE 10, 25
  2444.     PRINT "written entirely in QuickBASIC! "
  2445.     LOCATE 12, 21
  2446.     PRINT "(press any key for the 'Expand' routine)"
  2447.     SLEEP
  2448.  
  2449.     COLOR 0, 3
  2450.     Expand 2, 5, 22, 75
  2451.  
  2452.     COLOR 4
  2453.     LOCATE 8, 12
  2454.     PRINT "This is the 'Expand' routine. Like 'Explode' it calls"
  2455.     LOCATE 9, 12
  2456.     PRINT "the 'Drawbox' routine. It expands to it's full horizontal"
  2457.     LOCATE 10, 12
  2458.     PRINT "width  *before*  it begins to expand vertically though."
  2459.     LOCATE 13, 12
  2460.     PRINT "(press any key for the 'UnScroll' routine)"
  2461.     SLEEP
  2462.  
  2463.     COLOR 0, 7
  2464.     UnScroll 2, 3, 23, 78
  2465.     LOCATE 8, 12
  2466.     PRINT "This is the 'UnScroll' routine. Like 'Expand' it calls"
  2467.     LOCATE 9, 12
  2468.     PRINT "the 'Drawbox' routine. It expands to it's full vertical"
  2469.     LOCATE 10, 12
  2470.     PRINT "height  *before*  it begins to expand horizontally though."
  2471.     LOCATE 13, 12
  2472.     PRINT "(press any key for some quasi-fancy screen clearing)"
  2473.  
  2474.     SLEEP
  2475.  
  2476.     ScreenClear 3
  2477.  
  2478.  '+++++++++++++++++++++ windowing subs begin here ++++++++++++++++++++++
  2479.  
  2480.  ' This routine draws a double line box to the dimensions set
  2481.  ' in UpRow%, LtCol%, LoRow% and RtCol%. If you want a single line box
  2482.  ' just change the ascii chars, e.g. change CHR$(205) to CHR$(196) etc.
  2483.  
  2484.  SUB Drawbox (UpRow%, LtCol%, LoRow%, RtCol%) STATIC
  2485.     Wide% = (RtCol% - LtCol%) - 1
  2486.     LOCATE UpRow%, LtCol%
  2487.     PRINT CHR$(201); STRING$(Wide%, CHR$(205)); CHR$(187);
  2488.     FOR I% = UpRow% + 1 TO LoRow% - 1
  2489.         LOCATE I%, LtCol%
  2490.         PRINT CHR$(186); SPACE$(Wide%); CHR$(186);
  2491.     NEXT I%
  2492.     LOCATE LoRow%, LtCol%
  2493.     PRINT CHR$(200); STRING$(Wide%, CHR$(205)); CHR$(188);
  2494.  END SUB
  2495.  
  2496.  ' This routine will "expand" the window onto the screen calling on
  2497.  ' DRAWBOX to draw sucessively wider boxes until it hits the width
  2498.  ' dimensions. Then it will expand to meet the vertical dimensions.
  2499.  '
  2500.  SUB Expand (UpRow%, LtCol%, LoRow%, RtCol%) STATIC
  2501.     RowCenter% = ((LoRow% - UpRow%) / 2) + UpRow%
  2502.     ColCenter% = ((RtCol% - LtCol%) / 2) + LtCol%
  2503.     UprRow% = RowCenter%: LeftCol% = ColCenter%
  2504.     LwrRow% = RowCenter%: RghtCol% = ColCenter%
  2505.     DO
  2506.         LeftCol% = LeftCol% - 1
  2507.         RghtCol% = RghtCol% + 1
  2508.         IF LeftCol% < LtCol% THEN LeftCol% = LtCol%
  2509.         IF RghtCol% > RtCol% THEN RghtCol% = RtCol%
  2510.         Drawbox UprRow%, LeftCol%, LwrRow%, RghtCol%
  2511.         IF LeftCol% = LtCol% AND RghtCol% = RtCol% THEN EXIT DO
  2512.     LOOP
  2513.     DO
  2514.         UprRow% = UprRow% - 1
  2515.         LwrRow% = LwrRow% + 1
  2516.         IF UprRow% < UpRow% THEN UprRow% = UpRow%
  2517.         IF LwrRow% >= LoRow% THEN LwrRow% = LoRow%
  2518.         Drawbox UprRow%, LeftCol%, LwrRow%, RghtCol%
  2519.         IF UprRow% = UpRow% AND LwrRow% = LoRow% THEN EXIT DO
  2520.         Pause 1
  2521.     LOOP
  2522.     Shadow UpRow%, LtCol%, LoRow%, RtCol%
  2523.  END SUB
  2524.  
  2525.  ' This routine will "Explode" the window onto the screen calling on
  2526.  ' DRAWBOX to draw sucessively larger boxes until it hits the limits
  2527.  ' set in UpRow%, LtCol%, LoRow% and RtCol%. The first few lines determine
  2528.  ' where the approximate center of the box begins even if the window is
  2529.  ' to be located off-center with respect to the screen.
  2530.  '
  2531.  SUB Explode (UpRow%, LtCol%, LoRow%, RtCol%) STATIC
  2532.     RowCenter% = ((LoRow% - UpRow%) / 2) + UpRow%
  2533.     ColCenter% = ((RtCol% - LtCol%) / 2) + LtCol%
  2534.     UprRow% = RowCenter%: LeftCol% = ColCenter%
  2535.     LwrRow% = RowCenter%: RghtCol% = ColCenter%
  2536.     DO
  2537.         UprRow% = UprRow% - 1
  2538.         LeftCol% = LeftCol% - 3
  2539.         LwrRow% = LwrRow% + 1
  2540.         RghtCol% = RghtCol% + 3
  2541.         IF UprRow% < UpRow% THEN UprRow% = UpRow%
  2542.         IF LeftCol% < LtCol% THEN LeftCol% = LtCol%
  2543.         IF LwrRow% > LoRow% THEN LwrRow% = LoRow%
  2544.         IF RghtCol% > RtCol% THEN RghtCol% = RtCol%
  2545.         Drawbox UprRow%, LeftCol%, LwrRow%, RghtCol%
  2546.         IF UprRow% = UpRow% AND LeftCol% = LtCol% THEN
  2547.             IF LwrRow% = LoRow% AND RghtCol% = RtCol% THEN
  2548.                 EXIT DO
  2549.             END IF
  2550.         END IF
  2551.         Pause 1
  2552.     LOOP
  2553.     Shadow UpRow%, LtCol%, LoRow%, RtCol%   '*** now give it a Shadow ****
  2554.  END SUB
  2555.  
  2556.  '*** Just a little pauser that some of the routines
  2557.  '*** need so that they do not execute way too fast.
  2558.  SUB Pause (ticks%)
  2559.     DEF SEG = 0
  2560.     DO UNTIL TestTick% > ticks%
  2561.         IF LEN(INKEY$) THEN EXIT SUB
  2562.         LastTick% = GetTick%
  2563.         GetTick% = PEEK(&H46C)
  2564.         IF LastTick% <> GetTick% THEN
  2565.             TestTick% = TestTick% + 1
  2566.         END IF
  2567.     LOOP
  2568.  
  2569.  
  2570.     DEF SEG
  2571.  END SUB
  2572.  
  2573.  'This routine will do a little fancy screen clearing by simulating
  2574.  'an old style 1950s TV set being shut off. Screen shrinks to a single
  2575.  'horizontal line then disappears to a shrinking dot and is gone.
  2576.  'I wrote it for 80x25 text mode so if your displaying more screen lines
  2577.  'than 25 you'll have to play with it to get it to erase them all.
  2578.  '
  2579.  SUB ScreenClear (LineColor%) STATIC
  2580.     LOCATE , , 0
  2581.     DIM Lines$(1 TO 23)
  2582.     Lines$(1) = STRING$(80, CHR$(196))
  2583.     Sp% = 2
  2584.     Length% = 76
  2585.     FOR I% = 2 TO 21
  2586.         Lines$(I%) = SPACE$(Sp%) + STRING$(Length%, CHR$(196)) + "  "
  2587.         Sp% = Sp% + 2
  2588.         Length% = Length% - 4
  2589.     NEXT I%
  2590.     Lines$(22) = SPACE$(39) + CHR$(179) + SPACE$(2)
  2591.     Lines$(23) = SPACE$(39) + "." + SPACE$(2)
  2592.     COLOR 0, 0
  2593.     x% = 1
  2594.     y% = 25
  2595.     FOR I% = 1 TO 12
  2596.         LOCATE y%, 1
  2597.         PRINT STRING$(80, CHR$(32));
  2598.         LOCATE x%, 1
  2599.         PRINT STRING$(80, CHR$(32));
  2600.         Pause 1
  2601.         x% = x% + 1
  2602.         y% = y% - 1
  2603.     NEXT I%
  2604.     COLOR LineColor%, 0
  2605.     FOR I% = 1 TO 23
  2606.         LOCATE 13, 1
  2607.         PRINT Lines$(I%);
  2608.         Pause 1
  2609.     NEXT I%
  2610.     COLOR 7
  2611.     LOCATE , , 1, 6, 7
  2612.     CLS
  2613.  END SUB
  2614.  
  2615.  ' This routine creates a transparent Shadow along the right side
  2616.  ' and bottom edge of the box. Note: Special thanks to John Strong
  2617.  ' for his very helpful tips on what to POKE and where.
  2618.  '
  2619.  SUB Shadow (UpRow%, LtCol%, LoRow%, RtCol%) STATIC
  2620.     DEF SEG = &H40
  2621.     mono% = PEEK(&H10)
  2622.     IF (mono% AND 48) = 48 THEN
  2623.         EXIT SUB            '*** Forget the Shadow if it's monochrome.
  2624.     ELSE
  2625.         DEF SEG = &HB800
  2626.     END IF
  2627.  
  2628.  '****** find out what the screen attributes already are ****
  2629.  
  2630.     attr% = SCREEN(LoRow% + 1, RtCol% + 1, -1)  ' Get the attribute.
  2631.     attr% = attr% AND 15                     ' Calculate forground.
  2632.     attr% = attr% - 8                        ' Remove bright.
  2633.     IF attr% < 1 THEN attr% = 8              ' In case color wasn't bright.
  2634.  
  2635.  '****** use the given box dimensions to POKE a ***********
  2636.  '****** Shadow on the right side and bottom edge *********
  2637.  
  2638.     FOR row% = UpRow% + 1 TO LoRow% + 1       '***** right edge locations.
  2639.         FOR Col% = RtCol% + 1 TO RtCol% + 2   '***** make it 2 chars Wide.
  2640.             offset% = (row% - 1) * 160 + (Col% - 1) * 2 + 1
  2641.             POKE offset%, attr%
  2642.         NEXT
  2643.     NEXT
  2644.     row% = LoRow% + 1                        '***** now POKE along the
  2645.     FOR Col% = LtCol% + 2 TO RtCol% + 2      '***** bottom edge
  2646.         offset% = (row% - 1) * 160 + (Col% - 1) * 2 + 1
  2647.         POKE offset%, attr%
  2648.     NEXT
  2649.     DEF SEG
  2650.  END SUB
  2651.  
  2652.  ' This routine will "unscroll" the window onto the screen calling
  2653.  ' on DRAWBOX to draw sucessively taller boxes until it hits the height
  2654.  ' dimension. Then it will expand to meet the horizontal dimensions.
  2655.  '
  2656.  SUB UnScroll (UpRow%, LtCol%, LoRow%, RtCol%)
  2657.     RowCenter% = ((LoRow% - UpRow%) \ 2) + UpRow%
  2658.     ColCenter% = ((RtCol% - LtCol%) \ 2) + LtCol%
  2659.     UprRow% = RowCenter%: LeftCol% = ColCenter%
  2660.     LwrRow% = RowCenter%: RghtCol% = ColCenter%
  2661.     DO
  2662.         UprRow% = UprRow% - 1
  2663.         LwrRow% = LwrRow% + 1
  2664.         IF UprRow% < UpRow% THEN UprRow% = UpRow%
  2665.         IF LwrRow% >= LoRow% THEN LwrRow% = LoRow%
  2666.         Drawbox UprRow%, LeftCol% - 1, LwrRow%, RghtCol% + 1
  2667.         IF UprRow% = UpRow% AND LwrRow% = LoRow% THEN EXIT DO
  2668.     LOOP
  2669.     DO
  2670.         LeftCol% = LeftCol% - 1
  2671.         RghtCol% = RghtCol% + 1
  2672.         IF LeftCol% < LtCol% THEN LeftCol% = LtCol%
  2673.         IF RghtCol% > RtCol% THEN RghtCol% = RtCol%
  2674.         Drawbox UprRow%, LeftCol%, LwrRow%, RghtCol%
  2675.         IF LeftCol% = LtCol% AND RghtCol% = RtCol% THEN EXIT DO
  2676.     LOOP
  2677.  
  2678.     Shadow UpRow%, LtCol%, LoRow%, RtCol%
  2679.  
  2680.  END SUB
  2681.  
  2682. '
  2683.  
  2684.  From:  EVANS MARTIN              Sent: 07-18-93 16:45
  2685.    To:  ALL                       Rcvd: -NO-
  2686.    Re:  CREDIT CARD NUMBER VALIDA
  2687.  
  2688. This is a complete working credit card number validation program.
  2689. This program does NOT validate available credit or even if the card
  2690. is active.  All it does is run a checksum on the number to determine
  2691. if it could be a valid credit card number.  Also, it determines
  2692. what type of card it is.  Have fun with the PD example.
  2693.  
  2694. '** Save this script to a file, edit out all of the non-QB related
  2695. '** text and execute it in a QB environment to retrieve CARDCHK.BAS
  2696. CLS:?STRING$(50,177):?"Creating: CARDCHK.BAS with PostIt! v2.9f"
  2697. DEFINT A-Z:FOR A=0 TO 6:P(A)=2^A:NEXT:OPEN "B",1,"CARDCHK.BAS"
  2698. T$="abcdefghijklmnopqrstuvwxyz":T$=T$+UCASE$(T$)+"0123456789()"
  2699. G"dX0unOqsobvvubIiqXwzHnxzGuMB0vMCGmMCLrwA0bYyHjhzG4wDTjwzY5ciGOd
  2700. G"iIWcidjxzKLgDdfMCKrsdkaciwfgBPr2qHjhzLaspGadiNasqZnxDTvgiPrhiPn
  2701. G"hiU9gDGegi2fgBPrgiJfMCK5sdkacibrci9actujvsnrcksrLuj1ejOmKCLrwA0
  2702. G"nuyYrgjPKciGCcisvwBVzxzGWwzHr2l0jxyPXgiIXwyUT2CU0GcGassgbsqKasp
  2703. G"GiIiGqfsf5eif5erGaciGaYjGq2BUDcDGi2B0HwzYbcDVbYyOv2yRbsAMbIB1Xg
  2704. G"BnOaiGm0u11wjG0diWaciGaciGaciGaciGaciGaciGaciGaYjGmfDHjhDGCxA0H
  2705. G"giWaIzVjhiJHwzJTgiZvxBU0GcGast1XgDPbhBPvMCG0diY0GcGaYjGaLCVnwzZ
  2706. G"nhiMj3BTbICPDgA0bcDVbcBLzgDU0GcGaIrpjfiPbspGWuroHsqKKciTasmGq1t
  2707. G"GeditrvrqbslXaciNasqSXgiKL2zPr3CGugEJvgC0bcBHnhDG8MBL5sdkaciGas
  2708. G"qG0dibn1qO0userckbrclGKglGetkPaslGqdoGaciGCcihvgDGegiZLMBNXwzGq
  2709. G"wANLgDU0GcGaciGmvrmv0qubYqbnvrGeudkaciGaYqbnvrGK0uG0diZ0GcGaciG
  2710. G"aciGacicfMBRnuyYrgjG0diIeuBLjxAJfMBGueEWjxzZnNinOaiGacidf0ufbss
  2711. G"tbspGqtdkaciGaciGaciGiuyUT2qHjhzKaspGiIvPnxyI0GcGaciGmuqtveijnf
  2712. G"i9asnnOaiGaciGaciGaIqH52AdfMCKrci9aIinf2C0vMCGmuyYrMinOaiGacidf
  2713. G"0ufbsrmnvrnOaiGaciGaciGaIqH52AdfMCKrci9aIiprhALjNinOaiGacif5erG
  2714. G"mvrmv0qu1GcGaciGKKrGeei8acmG8KuGeei(asoGqfsf5eiGaciGaciGaciNass
  2715. G"MbIBVrhiW0soS0GcGaciGaciGmett1GcGaciGaciGiurfbvdkaciGaciGacusLK
  2716. G"tubIigfwASvNCLbsAUbIvHXwAKfgDLbYqYvgzPrhidfMCKbIuVvhDP5wzI0GcGa
  2717. G"ciGaciGaLuj5evGicuSvwyZvgiK9giU9gDGuMB0vMCGqwyZHwzZbYBYbYCWf2yL
  2718. G"nhiP5giJjxzKLgDGmwyYrgiUvxBIvMCGywALXgzHisdkaciGaciGaYqYvgzPr3q
  2719. G"HjhzKaspGiIinOaiGaciGacif5erGaciGaciGaciGaciGaciGaciGaciNaciGqh
  2720. G"AL5giNvgDG8wD05sdkaciGasroreijzudkaciGasqG0dibbIkG0uDSrxAWXwALj
  2721. G"xdkaciGast1XgDPbhBPvMCG0diZaslG0uDSrxAWXwALjhiGaciGCciu92zNXwzG
  2722. G"0wDSrxAWXwALjxdkaciGassgbsqG4di5acvivKtnOaiGaciGaciuvwBWrci9act
  2723. G"ujvsnrcktrLuKGsqPKciGaciGaYjGeezKbIB11MyLj3CGq3BNvgDOvMCU0GcGac
  2724. G"iGaciGeei9acmnOaiGaciGacig9KuGOei9asmGq1tGWuroHcvL1gCKKsdkaciGa
  2725. G"ciGaciGeei9asqGScibn1qO0userckuvwBWrclGOelGetkPaslGqdoNaIrHnhDL
  2726. G"jhi0HwyUbIvbXKlnOaiGaciGaciovewu1GcGaciGuKtebssg1GcGaciGm0u11wj
  2727. G"G0didnvDTvciRasqnOaiG4uryrvdkaciNaItVDhi3vgiZvNy0jxyJrhi0HwzGmg
  2728. G"ALn2AZvxBnOaiGCciMj3BTbcDOvgiUvgE0bcAPDgALjhiTvhB0LgCSvgiVzgiXa
  2729. G"JlnOaiGm0u11wjG0diOGYqtvxBLaYkGKtkGWfiXatkGOciXadiTaYqtvxBL0GcG
  2730. G"assgbIvbXeksL0rirfjOeejSasmPKci9aYqtvxBLacvivKtnOaiGaciGyvySLgz
  2731. G"dfMCKvci9aslX0GcGaciGacusLKtu1GcGaciGacusLKtubIidfMCKbciGaIoGic
  2732. G"iRasvdf0ufrckcfMBRnuyYrgjP0GcGaciGacusLKtubIibn2y0bItVbIoGiciRa
  2733. G"svdf0ufrckbrsknOaiGaciGaLuj5evGiYu0fgD1nhiGOdiIStdkaciGacid9etp
  2734. G"jfiXqdlGatdkaciGaciqjvsorfiIavyZnxzKbIDHXwAKfgDP9MBHisdkacifX0u
  2735. G"f1GcGaciGacusLKtu1GcGaciGaIqfveunOaiGaciGaLuj5evGiYqHjhzGaciGOd
  2736. G"ij5MDHXwAKbYqHjhzI0GcGaciGacusLKtubIibn2y0bItVbIoGiciRasvdf0ufr
  2737. G"ckbrsknOaiGaciGaLuj5evGiYu0fgD1nhiGOdiIStdkaciGacid9etpjfiYGdlG
  2738. G"atdkaciGaciqjvsorfiIyuyPXwzKbIDHXwAKfgDP9MBHisdkaciGacicfMBRnuy
  2739. G"YrgjG0diIisdkacif5erGKKrnOaiGm0tm9KuGCdlGatdk0Gc"
  2740. N=1926:K=255:IF LEN(C$)<>2568 THEN ?"Incomplete script file!":BEEP:END
  2741. FOR A=1 TO N:IF L=0 THEN GOSUB G:L=6:LOCATE 1:?STRING$((51&*A)\N,8)
  2742. W=T\P(6-L):GOSUB G:W=W OR T*P(L):L=L-2:B$=CHR$(W AND K):PUT 1,,B$:NEXT
  2743. ?:IF C<>76 THEN ?"Bad checksum!":BEEP:END ELSE ?"Success!":END
  2744. G:I=I+1:T=INSTR(T$,MID$(C$,I,1))-1:C=(C+T)*2:C=C\256+(C AND 255):RETURN
  2745. SUB G(A$):SHARED C$:C$=C$+LEFT$(A$,63):END SUB
  2746. '
  2747.  
  2748. ' From:  REUVEN LAX                Sent: 07-18-93 15:28
  2749. '   To:  VICTOR YIU                Rcvd: -NO-
  2750. '   Re:  SORTING
  2751. '
  2752. 'I wrote a sort routine that is faster than any other I've seen.  Can
  2753. 'you come up with anything faster?  (actually it uses the same Idea as A
  2754. 'quickSort but this is not recursive).  Here it is!
  2755.   
  2756.  
  2757. DECLARE SUB MyQsort ()
  2758. '$DYNAMIC
  2759. DEFINT A-Z
  2760. DIM SHARED Array(1 TO 1), Start, Finish, Stack(1 TO 128)
  2761. FOR A = 1 TO 14
  2762.   Count = 2 ^ A
  2763.   Start = 1: Finish = Count
  2764.   REDIM Array(1 TO Count)
  2765.   RANDOMIZE TIMER
  2766.   FOR B = 1 TO Count
  2767.     Array(B) = RND * 32766 + 1
  2768.   NEXT
  2769.  
  2770.   T1! = TIMER
  2771.   MyQsort
  2772.    T2! = TIMER - T1!
  2773.   PRINT "MyQuickSort:  took"; T2!; "secs for"; Count; "numbers."
  2774. NEXT
  2775.  
  2776. REM $STATIC
  2777. SUB MyQsort
  2778.   sp = 0
  2779. DO
  2780.   IF Start - Finish = 1 THEN
  2781.     IF Array(Start) > Array(Finish) THEN SWAP Array(Start), Array(Finish)
  2782.   ELSE
  2783.   DO WHILE Start < Finish
  2784.       Top = Start: Bot = Finish
  2785.       Cmp = Array((Start + Finish) \ 2)
  2786.       DO
  2787.         DO WHILE Array(Top) < Cmp
  2788.           Top = Top + 1
  2789.         LOOP
  2790.         DO WHILE Cmp < Array(Bot)
  2791.           Bot = Bot - 1
  2792.         LOOP
  2793.         IF Bot >= Top THEN
  2794.           SWAP Array(Top), Array(Bot)
  2795.           Top = Top + 1: Bot = Bot - 1
  2796.         END IF
  2797.       LOOP UNTIL Top > Bot
  2798.       sp = sp + 1
  2799.       Stack(sp) = Finish
  2800.       Finish = Bot
  2801.     LOOP
  2802.   END IF
  2803.     Start = Top
  2804.     Finish = Stack(sp): sp = sp - 1
  2805.     LOOP WHILE sp > 0
  2806.  
  2807. END SUB
  2808. '
  2809.  
  2810. ' From:  JIM LITTLE                Sent: 07-17-93 03:02
  2811. '   To:  ALL                       Rcvd: -NO-
  2812. '   Re:  ANIMAL
  2813. '
  2814. 'Hi, All!
  2815. '
  2816. 'The next 3 posts are an interesting example of "intelligence imitation"
  2817. '(as I call it) in a guessing game-like program. You think of an
  2818. 'animal, and the computer attempts to guess it.  If you don't know the
  2819. 'trick, it seems to be quite intelligent.  Then you pick one it doesn't
  2820. 'know, and all becomes clear..
  2821. '
  2822. 'The fourth post is a PostIt! script of a sample data file to go with
  2823. 'the program.  You might want to try it with the data file, first, as
  2824. 'that will give you a better idea of how it works.  Then, delete
  2825. 'ANIMAL.DAT, and create your own, customized, artificially intelligent
  2826. 'database of animals!  <grin>
  2827. '
  2828. 'Have fun!  Jim
  2829.  
  2830. 'ANIMAL.BAS
  2831.  
  2832. 'by Jim Little
  2833.  
  2834. 'This code is not copyrighted.  However, if you use this code without major
  2835. ' modifications, please give me credit.
  2836.  
  2837. 'This code implements an old "Intelligence Imitating" algorithm I first
  2838. ' saw on the Apple II.  Undoubtably, the routine goes back farther than
  2839. ' that.  I've never seen it on the IBM, though, and thought it may be of
  2840. ' interest.
  2841.  
  2842. 'This was thrown together at 3:00 AM.. please excuse the poor design.
  2843.  
  2844. 'What does it do?  Run it and find out!
  2845.  
  2846. DECLARE SUB Intro ()
  2847. DECLARE SUB Main ()
  2848. DECLARE SUB GetChoice (choice%)
  2849. DECLARE SUB InitializeData ()
  2850.  
  2851. TYPE animal
  2852.    yes AS INTEGER
  2853.    no AS INTEGER
  2854.    animal AS STRING * 80
  2855. END TYPE
  2856.  
  2857. CONST cAssign = 1
  2858. CONST cRetrieve = 2
  2859.  
  2860. CONST False = 0
  2861. CONST True = NOT False
  2862.  
  2863. Intro
  2864. DO: k$ = INKEY$: LOOP UNTIL LEN(k$)
  2865. DO UNTIL k$ = CHR$(27)
  2866.    VIEW PRINT 5 TO 25
  2867.    Main
  2868.    VIEW PRINT 1 TO 25
  2869.    Intro
  2870.    DO: k$ = INKEY$: LOOP UNTIL LEN(k$)
  2871. LOOP
  2872.  
  2873. SUB GetChoice (choice%)
  2874. 'gets a y/n response from the user and returns it as -1 (for y) or 0 (for n)
  2875.  
  2876. LOCATE , , 1
  2877.  
  2878. DO
  2879.    k$ = INKEY$
  2880. LOOP WHILE UCASE$(k$) <> "Y" AND UCASE$(k$) <> "N"
  2881. choice% = (UCASE$(k$) = "Y")
  2882. PRINT k$;
  2883.  
  2884. LOCATE , , 0
  2885.  
  2886. END SUB
  2887.  
  2888. SUB InitializeData
  2889. 'initializes file #1 to include one question and two animals.
  2890.  
  2891. DIM animal AS animal    'the question/animals
  2892.  
  2893. animal.animal = "Does your animal swim a lot?"
  2894. animal.yes = 2
  2895. animal.no = 3
  2896. PUT #1, 1, animal
  2897.  
  2898. animal.animal = "goldfish"
  2899. animal.yes = -1
  2900. animal.no = -1
  2901. PUT #1, 2, animal
  2902.  
  2903. animal.animal = "cat"
  2904. animal.yes = -1
  2905. animal.no = -1
  2906. PUT #1, 3, animal
  2907.  
  2908. END SUB
  2909.  
  2910. SUB Intro
  2911.  
  2912. CLS
  2913. PRINT TAB(37); "Animal"
  2914. PRINT TAB(34); "by Jim Little"
  2915. PRINT
  2916. PRINT
  2917. PRINT "Think of an animal, and I will attempt to guess it.  As you play, I will"
  2918. PRINT "learn more animals.  Eventually, I will know every common animal."
  2919. PRINT
  2920. PRINT "Press any key when you've thought of an animal, or ESC to quit."
  2921.  
  2922. END SUB
  2923.  
  2924. SUB Main
  2925. 'This is the main program driver.
  2926.  
  2927. DIM animalnum&       'animal/question being checked/asked
  2928. DIM animal AS animal
  2929. DIM usersanimal$     'the user's animal/question
  2930. DIM companimal$      'the computer's animal/question
  2931. DIM newanimal AS animal  'new animal being added to data
  2932. DIM choice%          'yes or no response from user (yes is -1,  no is 0)
  2933. DIM records&         'number of records in ANIMAL.DAT
  2934.  
  2935. OPEN "ANIMAL.DAT" FOR RANDOM AS #1 LEN = LEN(animal)
  2936. IF LOF(1) = 0 THEN
  2937.    InitializeData
  2938. END IF
  2939.  
  2940. CLS
  2941. animalnum& = 1
  2942. GET #1, animalnum&, animal
  2943. companimal$ = RTRIM$(animal.animal$)
  2944. DO
  2945.    IF animal.yes <> -1 THEN 'question, not animal
  2946.       PRINT companimal$; " ";
  2947.       GetChoice choice%
  2948.       PRINT : PRINT
  2949.       IF choice% THEN  'choice = 'YES'
  2950.          animalnum& = animal.yes
  2951.       ELSE  'choice = 'NO'
  2952.          animalnum& = animal.no
  2953.       END IF
  2954.    END IF
  2955.    GET #1, animalnum&, animal
  2956.    companimal$ = RTRIM$(animal.animal$)
  2957. LOOP UNTIL animal.yes = -1
  2958.  
  2959. PRINT "Is your animal a "; companimal$; "? ";
  2960. GetChoice choice%
  2961. PRINT : PRINT
  2962. IF choice% THEN
  2963.    PRINT "Wow, I'm smart!"
  2964.    PRINT "(Press a key)"
  2965.    DO: LOOP UNTIL LEN(INKEY$)
  2966. ELSE
  2967.    LINE INPUT "What was your animal? ", useranimal$
  2968.    PRINT
  2969.    PRINT "Please type a yes/no question that differentiates a"
  2970.    PRINT useranimal$; " from a "; companimal$; ": ";
  2971.    LINE INPUT "", newanimal.animal
  2972.    PRINT
  2973.    PRINT "If your animal was a "; useranimal$; ", how would you answer"
  2974.    PRINT "the above question? ";
  2975.    GetChoice choice%
  2976.    records& = LOF(1) \ LEN(animal)
  2977.    newanimal.yes = records& + 1
  2978.    newanimal.no = records& + 2
  2979.    PUT #1, animalnum&, newanimal
  2980.    newanimal.animal = useranimal$
  2981.    newanimal.yes = -1
  2982.    newanimal.no = -1
  2983.    IF choice% THEN
  2984.       PUT #1, records& + 1, newanimal
  2985.       PUT #1, records& + 2, animal
  2986.    ELSE
  2987.       PUT #1, records& + 1, animal
  2988.  
  2989.       PUT #1, records& + 2, newanimal
  2990.    END IF
  2991. END IF
  2992.  
  2993. CLOSE #1
  2994.  
  2995. END SUB
  2996.  
  2997. '
  2998.  
  2999.  
  3000. ' From:  JIM LITTLE                Sent: 07-17-93 03:10
  3001. '   To:  ALL                       Rcvd: -NO-
  3002. '   Re:  ANIMAL.DAT
  3003.  
  3004. 'This is a sample data file for ANIMAL.BAS.  It isn't required,
  3005. 'but you may enjoy the program more (at first, at least) if
  3006. 'you unzip it into the directory containing ANIMAL.BAS.  Simply
  3007. 'execute in a QB-related environment to get ANIMAL.ZIP, then
  3008. 'unzip with PKUNZIP v2.04 or greater to get ANIMAL.DAT.
  3009. CLS:?STRING$(50,178):DEFINT A-Z 'Created by PostIt! 6.1
  3010. FOR A=0 TO 6:P(A)=2^A:NEXT:OPEN "B",1,"ANIMAL.ZIP
  3011. T$="abcdefghijklmnopqrstuvwxyz":T$=T$+UCASE$(T$)+"0123456789#$
  3012. G"qT0aeqb)icaCANXRrE1TM#My)WUl(Gc*b5usnfetUququ1Sw3Fj12aHNx69EVoH
  3013. G"uifinsVFbscqkKqi96y7X2kRSKpz5Bzj9Ep5p5elVl84wVoC238dz#byFlpM9tt
  3014. G"5BksNgC6W#SCfoZw7qYOkinwnvv8S4OLlbU0U6uR2GrLb95mUMXtoVkEa$gNbCM
  3015. G"D1ze5MSnW5NceSpvYMuP3nd9wKjFdI9EnJhMtWdRsuMmC$wBYP437SGZg2lz2Ko
  3016. G"t1Z2gOSrGBboWMp5egPCjOnflD8y0#5aNBhCwXNWdLmgl75bNFxFuo143ToYKXn
  3017. G"FvcgPP4jyL3Pk5Qt9pXfaxyp6uZzBH$$Rs#NgXRYyhkNucR31hLBRRySK9JdNxe
  3018. G"CXRm30KHPoBb6D8wXeUj5oGD2tUzTjHDzoBZFpqDgSNfwZeEgkMCvWv3fNHSYuv
  3019. G"v#iZMUt4U#V5LMsE2nmLFzWL3t85uokItWuEWU$NdwdswwNRxtXBdYURaUYoCDv
  3020. G"EYfo674G00QDmCDh87v5KBckNCtWn3J9mvzgz##oGBTBUzdWt4abQJhA8EqUg4A
  3021. G"xwNrQSaNIpNmn6ViK7pmfFdWn2ZzNqVR2mg3$1bx$QI5NWHQCrkpJj2HYJg87PA
  3022. G"MqbL7hEGEY31QbtYDs4K1CzJdXJcj3nCpl735J6vM4rYXhKuiRB8zbv08HrR07f
  3023. G"UVEILyvwUFuvpFi4H7QZKa$zIDQjeqft#48b1sYtbpDVX8PKjEg6j1andTXN2JI
  3024. G"sk3gC7DX5uMWyT1NpUi0aoZRlidkN8eWtUCS00CLFGmrDX5uRRafu2h8cDTNXKZ
  3025. G"nRLBM1Ji0$TH3z5ZEHk2zRITLdAyJLo7LSjRwjNx6XGhVB#UFwzB5OqF9JaQ7c2
  3026. G"DxD6SjSz8etpiSNL1PXg7RDMXtm9iWJ21V7S0aB7QJpQbx2Y6kuk5zGNTV#q0QY
  3027. G"s2nIm#2#Lkvjso4XbHxU$EggmZ5jXlrfvEbbAqNokksjwL4f88Ko2kibAcWIhZh
  3028. G"WPwjjkr85H9UmoDQKe2GrZW2IkdpanGZeBIffu#n437YlvsXt2DETXhgLnMCg1g
  3029. G"w3wVevlNSvtomIjHcrd6mZPo2X0ZetRZRDqX2y5Mq69Hpyz7zZFKPmKB0lz6RGV
  3030. G"w8DR9IWl1FFDw3eob9wtw9qAmPn#SwqJzJ8dWpcR54tgM7SQ5WhPAWEmP1nanEw
  3031. G"MH9B98P3fEVvS3Ms12yebC$WbwHo9HnQznOFQDelAOTEKHuAuo5iWB1JFVQXAo#
  3032. G"#KBW5egLtc6SX7QYiVvQQCVgCOvmRnnd5SXUQs8vGxTRoZcZCvO0JmbD##6cRMu
  3033. G"oP1Ek3y2NqNX2PIQZxhEJE5LQYTo$UW4ANEqJDQHIv0tBTb3GDZW1RvXtg8na4G
  3034. G"Q#Mps5B$wtwy3DewARJ3ABeTTEKkdLuom8M9T$tRvJP1JOX#57vK5suo5JHpPx$
  3035. G"EJB38$jEPkDTOoP29GvNPzJCRSlOZd5IfDhGT57ublz9O2zzSoX56sCuMKD0$HW
  3036. G"h1pVuOp5HEXelWzel5jhXnGPIFVmrq8k7PL2gn1MdQETNARjdn8GVoL57btNIck
  3037. G"ZN5swo5GoRnQyRtUa0253ZvgjhoSDVntLmBFbxhLPLoFFAojQo$f4xxDEePZOrU
  3038. G"T22zyvoknvoK#zWKEYJwVwP1JUF#sRlUUuzKcPgW2do91P#QWMvtB#hSM22NyAR
  3039. G"S$U24ZWlviohLsARXLRINWYMBQNze9cp8qDF8NgVEuR$Cr9D5NrOSbs5cVRYjKj
  3040. G"JCwXO7AN5Ivojp$T1oyrFgl4XGpV$3iXA1vEhPv0i2Zatz1A546pkmB18NjmLGf
  3041. G"nFACZD0#KDs1I9sw#g4B7S$ttJTSIh$ng3gFAjnj4#p$c4lxT9mIkIS2rb1f5Mj
  3042. G"sIZ$a#ZU4SWUbpFy7kTD6GN1Er8POnI8xWFFkUFZ51IAx24wSt02#5R9E2jxa63
  3043. G"bFFN3Za5ztaDhjNmtQYuBOmzdXK2##6QDI#EW#j4N78EfZBC7UJ$2Ti1GJWA97t
  3044. G"f963N3dM1k#8M51ffnUH2x9Py9ZBJe8wTxKVRzBSTYtfYCzSZTN6KW4LOqY87IS
  3045. G"uUf$E8CtN14TvuTtUZ#E2T3YjbEnViN$53vo3Ooumzb$j6RDEPgsA#CX6YCbJq$
  3046. G"xaqTuacqbau*G(N2z8AKxDRPVjg)S7c(O/ab(i/eKtj1uqm5crbrfulvGb+e(b(
  3047. G"o*oz,"
  3048. N=1692:K=255:IF LEN(C$)<>2256 THEN ?"Bad script!":END
  3049. FOR A=1 TO N:LOCATE 1:?STRING$(50/N*A,177):IF L=0 THEN GOSUB G:L=6
  3050. W=T\P(6-L):GOSUB G:W=W OR T*P(L):L=L-2:B$=CHR$(W AND K):PUT 1,,B$:NEXT
  3051. ?:IF C=74 THEN ?"Ok":END ELSE ?"Bad checksum!":END
  3052. G:I=I+1:T=INSTR(T$,MID$(C$,I,1))-1:C=(C+T)*2:C=C\256+(C AND 255):RETURN
  3053. SUB G(A$):SHARED C$:FOR Q=2 TO 9:DO:S=INSTR(A$,CHR$(Q+38))
  3054. IF S THEN A$=LEFT$(A$,S-1)+STRING$(Q,97)+MID$(A$,S+1)
  3055. LOOP WHILE S:NEXT:C$=C$+A$:END SUB
  3056.  
  3057. '
  3058.  
  3059. ' From:  CHRIS LATTNER             Sent: 07-18-93 11:41
  3060. '   To:  ALL                       Rcvd: -NO-
  3061. '   Re:  FUN & GAMES!
  3062. '
  3063. '    Hi everybody!  I recently downloaded the QuickBasic Scrapbook #3 and
  3064. 'saw Quinn's Fun & games program.... I figured why not make this a
  3065. 'general purpose file for playing music.  So I altered it so you can
  3066. 'choose the music you want to play, and added the William Tell Overature.
  3067. 'Here it is:
  3068.  
  3069. DEFINT A-Z
  3070.  
  3071. ON PLAY(3) GOSUB Music
  3072. PLAY ON
  3073.  
  3074. PLAY "O3MB"
  3075. EndOfMusic = 1: CLS
  3076. SCREEN 13
  3077.  
  3078. DO
  3079.     IF EndOfMusic = 1 THEN
  3080.          GOSUB Menu
  3081.     END IF
  3082.     IF RND > .3 THEN
  3083.          CenterPointX = RND * 320
  3084.          CenterPointY = RND * 200
  3085.     FOR A = 1 TO 250
  3086.          LINE (CenterPointX, CenterPointY)-(RND * 320, RND * 200), RND * 255
  3087.     NEXT A
  3088. ELSE
  3089.     FOR A = 1 TO RND * 75
  3090.          LINE -(RND * 320, RND * 200), RND * 255, BF
  3091.     NEXT A
  3092. END IF
  3093.  
  3094. IF INKEY$ <> "" THEN GOSUB Menu
  3095. LOOP
  3096.  
  3097. Menu:
  3098.     PLAY OFF
  3099.     DO UNTIL INKEY$ = "": LOOP 'Clear the keyboard buffer
  3100.     SCREEN 0: WIDTH 80: CLS
  3101.     LOCATE 24, 1
  3102. Menu2:
  3103.     PRINT "What shall we do?"
  3104.     PRINT
  3105.     PRINT "   0. End"
  3106.     PRINT "   1. Play Hello My Baby"
  3107.     PRINT "   2. Play an Italian song"
  3108.     PRINT "   3. Play the Hava Nagilah"
  3109.     PRINT "   4. Play the William tell Overature"
  3110.     PRINT ""
  3111.     INPUT "Hmm... ", A$
  3112.     A$ = UCASE$(LEFT$(A$, 1))
  3113.     SELECT CASE A$
  3114.          CASE "0"
  3115.               CLS
  3116.               PRINT "Chris Lattner 1993"
  3117.               PRINT "Modified and added to code by Quinn Tyler Jackson"
  3118.               END
  3119.          CASE "1", "A"
  3120.               RESTORE HelloMyBabyData
  3121.          CASE "2", "B"
  3122.               RESTORE ItalianData
  3123.          CASE "3", "C"
  3124.               RESTORE HavaNagilahData
  3125.          CASE "4", "D"
  3126.               RESTORE WillTell
  3127.          CASE ELSE
  3128.               PRINT : PRINT "What?  I don't understand!": PRINT
  3129.               GOTO Menu2
  3130.          END SELECT
  3131.     EndOfMusic = 0
  3132.     SCREEN 13
  3133.     PLAY ON
  3134.     GOSUB Music
  3135. RETURN
  3136.  
  3137. Music:
  3138. READ Bar$
  3139. IF Bar$ = "END_OF_SONG" THEN
  3140.     EndOfMusic = 1
  3141. ELSE
  3142.     PLAY Bar$
  3143. END IF
  3144. RETURN
  3145.  
  3146. HelloMyBabyData:
  3147. DATA "MlT200<C8MNDP32.MlC8DC<A8MNB-P32.MlA8B-AP32G8MNAP32.MlG8AGD<BAG>P32A8G"
  3148. DATA "P32F#8AG>C<<B-AG>P32F8AP32>C8EDC<<GAB->>C8MNDP32.MlC8DC<A8MNB-P32."
  3149. DATA "MlA8B-AP32G8MNAP32.MlG8AGDP32.DEFA8A8G2P32F#AG>T190MNCP64"
  3150. DATA "Ml<AF2<C8DC8DF2P32E32E-32D32D-32C32<B32B-32A32A-32G32G-32FP4>>"
  3151. DATA "END_OF_SONG"
  3152.  
  3153. ItalianData:
  3154. DATA "<<T225MSG8>MNC<E8EE8EMS>C8<BB8GG8AA8MNEP8MlE2MNP8E8DC8C<G8GG8GP16>E8DCC<G8GG8GG8"
  3155. DATA "G>MSG8>MNC<E8EE8E>C8<BB8MSGMNG8MSAMNA8EP8MlE2MNP32E8DC8C<G8GG8G8>P8E8D8C<G8GG8G"
  3156. DATA "G8G>MSE8E<MNB8BB8B>MSE8F#F#8MNEMSE8MNF#F#8E.MlE2.MNP8<B8BB8MlB1MNP8B8B8BG8GG8GG8GP16MS>G8
  3157. DATA "MNGD8DD8DMSG8AMNA8GG8AA8GD8GD8DMSD8MNDD8D<B8BB8BP16>D8DD8D<B8BB8BP16"
  3158. DATA "B8BB8>MlB.A8P4B.A8P4>MSCMN<B8A>C8<B<<D8<MlBA8>>B.A8MNP4B.A8P4>MSCMN<B8SA>C8<MSGE8EE8MNEE8EE8EE8EE8E"
  3159. DATA "E8EE8>C1DC8<A>C8<MSGE8MNEF8MSGMNF8ED8MlC1P4
  3160. DATA "END_OF_SONG"
  3161.  
  3162. HavaNagilahData:
  3163. DATA "T150<<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P64E8D.P16G-8P64G-E-8D8P64"
  3164. DATA "D8P64D8P8E-8P64E-D8C8P64C8P64C8P8CE-D16C8P64C8GP64G-E-8P64E-8DP4MNG2G2
  3165. DATA "MlG16P64G16P64MNG8P32MSB-8P16A16MNG8B-8A8G8P64MlG16P64G16P64MNG8P32MSB8P16A16MNG8B-8A8G8P64"
  3166. DATA "MlA16P64A16P64MNA8P32MS>C8P16<MNB-16A8>C8<B-8A8P64MlA16P64A16P64MNA8P3MS>C8P16<MNB-16A8>C8<B-8A8P64"
  3167. DATA "A16A16MSA8MN>D2<A16A16MSA8MN>D2<A16A16MNA8MN>D2.D-32MlC64<B64B-64A64A-4G64G-64F64E64E-64"
  3168. DATA "T170>><<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P6E-8D.P16G-8P64G-E-8D8P64"
  3169. DATA "D8P64D8P8E-8P64E-D8C8P64C8P64C8P8CE-D16C8P64C8GP64G-E-8P64E-8DP4MNG2G2
  3170. DATA "MlG16P64G16P64MNG8P32MSB-8P16A16MNG8B-8A8G8P64MlG16P64G16P64MNG8P32MSB8P16A16MNG8B-8A8G8P64"
  3171. DATA "MlA16P64A16P64MNA8P32MS>C8P16<MNB-16A8>C8<B-8A8P64MlA16P64A16P64MNA8P3MS>C8P16<MNB-16A8>C8<B-8A8P64"
  3172. DATA "A16A16MSA8MN>D2<A16A16MSA8MN>D2<A16A16MNA8MN>D2.D-32MlC64<B64B-64A64A-4G64G-64F64E64E-64"
  3173. DATA "T190>><<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P6E-8D.P16G-8P64G-E-8D8P64"
  3174.  
  3175. '
  3176.  
  3177. 'Msg #:  1447                      QUIKBAS Subboard
  3178. ' From:  CHRIS LATTNER             Sent: 07-18-93 11:46
  3179. '   To:  ALL                       Rcvd: -NO-
  3180. '   Re:  FUN & GAMES
  3181. '
  3182. '    Hi all!  I have edited Quinn's fun and games program to allow you
  3183. 'to choose which song to play and I have added a song.  Here it is:
  3184. '
  3185. DEFINT A-Z
  3186. .
  3187. ON PLAY(3) GOSUB Music
  3188. PLAY ON
  3189.  
  3190. PLAY "O3MB"
  3191. EndOfMusic = 1: CLS
  3192. SCREEN 13
  3193. .
  3194. DO
  3195.         IF EndOfMusic = 1 THEN
  3196.                 GOSUB Menu
  3197.         END IF
  3198.         IF RND > .3 THEN
  3199.                 CenterPointX = RND * 320
  3200.                 CenterPointY = RND * 200
  3201.         FOR A = 1 TO 250
  3202.                 LINE (CenterPointX, CenterPointY)-(RND * 320, RND * 200), RND * 255
  3203.         NEXT A
  3204.         ELSE
  3205.         FOR A = 1 TO RND * 75
  3206.                 LINE -(RND * 320, RND * 200), RND * 255, BF
  3207.         NEXT A
  3208.         END IF
  3209.  
  3210. IF INKEY$ <> "" THEN GOSUB Menu
  3211. LOOP
  3212.  
  3213. Menu:
  3214.         PLAY OFF
  3215.         DO UNTIL INKEY$ = "": LOOP 'Clear the keyboard buffer
  3216.         SCREEN 0: WIDTH 80: CLS
  3217.         LOCATE 24, 1
  3218. Menu2:
  3219.         PRINT "What shall we do?"
  3220.         PRINT
  3221.         PRINT "   0. End"
  3222.         PRINT "   1. Play Hello My Baby"
  3223.         PRINT "   2. Play an Italian song"
  3224.         PRINT "   3. Play the Hava Nagilah"
  3225.         PRINT "   4. Play the William tell Overature"
  3226.         PRINT ""
  3227.         INPUT "Hmm... ", A$
  3228.         A$ = UCASE$(LEFT$(A$, 1))
  3229.         SELECT CASE A$
  3230.                 CASE "0"
  3231.                         CLS
  3232.                         PRINT "Chris Lattner 1993"
  3233.                         PRINT "Modified and added to code by Quinn Tyler Jackson"
  3234.                         END
  3235.                 CASE "1", "A"
  3236.                         RESTORE HelloMyBabyData
  3237.                 CASE "2", "B"
  3238.                         RESTORE ItalianData
  3239.                 CASE "3", "C"
  3240.                         RESTORE HavaNagilahData
  3241.                 CASE "4", "D"
  3242.                         RESTORE WillTell
  3243.                 CASE ELSE
  3244.                         PRINT : PRINT "What?  I don't understand!": PRINT
  3245.                         GOTO Menu2
  3246.                 END SELECT
  3247.         EndOfMusic = 0
  3248.         SCREEN 13
  3249.         PLAY ON
  3250.         GOSUB Music
  3251. RETURN
  3252. .
  3253. Music:
  3254. READ Bar$
  3255. IF Bar$ = "END_OF_SONG" THEN
  3256.         EndOfMusic = 1
  3257. ELSE
  3258.         PLAY Bar$
  3259. END IF
  3260. RETURN
  3261.  
  3262. HelloMyBabyData:
  3263. DATA "MlT200<C8MNDP32.MlC8DC<A8MNB-P32.MlA8B-AP32G8MNAP32.MlG8AGD<BAG>P32A8G
  3264. DATA "P32F#8AG>C<<B-AG>P32F8AP32>C8EDC<<GAB->>C8MNDP32.MlC8DC<A8MNB-P32."
  3265. DATA "MlA8B-AP32G8MNAP32.MlG8AGDP32.DEFA8A8G2P32F#AG>T190MNCP64"
  3266. DATA "Ml<AF2<C8DC8DF2P32E32E-32D32D-32C32<B32B-32A32A-32G32G-32FP4>>"
  3267. DATA "END_OF_SONG"
  3268.  
  3269. ItalianData:
  3270. DATA "<<T225MSG8>MNC<E8EE8EMS>C8<BB8GG8AA8MNEP8MlE2MNP8E8DC8C<G8GG8GP16>E8DCC<G8GG8GG8"
  3271. DATA "G>MSG8>MNC<E8EE8E>C8<BB8MSGMNG8MSAMNA8EP8MlE2MNP32E8DC8C<G8GG8G8>P8E8D8C<G8GG8G"
  3272. DATA "G8G>MSE8E<MNB8BB8B>MSE8F#F#8MNEMSE8MNF#F#8E.MlE2.MNP8<B8BB8MlB1MNP8B8B8BG8GG8GG8GP16MS>G8
  3273. DATA "MNGD8DD8DMSG8AMNA8GG8AA8GD8GD8DMSD8MNDD8D<B8BB8BP16>D8DD8D<B8BB8BP16"
  3274. DATA "B8BB8>MlB.A8P4B.A8P4>MSCMN<B8A>C8<B<<D8<MlBA8>>B.A8MNP4B.A8P4>MSCMN<B8SA>C8<MSGE8EE8MNEE8EE8EE8EE8E"
  3275. DATA "E8EE8>C1DC8<A>C8<MSGE8MNEF8MSGMNF8ED8MlC1P4
  3276. DATA "END_OF_SONG"
  3277.  
  3278. HavaNagilahData:
  3279. DATA "T150<<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P64E8D.P16G-8P64G-E-8D8P64"
  3280. DATA "D8P64D8P8E-8P64E-D8C8P64C8P64C8P8CE-D16C8P64C8GP64G-E-8P64E-8DP4MNG2G2
  3281. DATA "MlG16P64G16P64MNG8P32MSB-8P16A16MNG8B-8A8G8P64MlG16P64G16P64MNG8P32MSB8P16A16MNG8B-8A8G8P64"
  3282. DATA "MlA16P64A16P64MNA8P32MS>C8P16<MNB-16A8>C8<B-8A8P64MlA16P64A16P64MNA8P3MS>C8P16<MNB-16A8>C8<B-8A8P64"
  3283. DATA "A16A16MSA8MN>D2<A16A16MSA8MN>D2<A16A16MNA8MN>D2.D-32MlC64<B64B-64A64A-4G64G-64F64E64E-64"
  3284. DATA "T170>><<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P6E-8D.P16G-8P64G-E-8D8P64"
  3285. DATA "D8P64D8P8E-8P64E-D8C8P64C8P64C8P8CE-D16C8P64C8GP64G-E-8P64E-8DP4MNG2G2
  3286. DATA "MlG16P64G16P64MNG8P32MSB-8P16A16MNG8B-8A8G8P64MlG16P64G16P64MNG8P32MSB8P16A16MNG8B-8A8G8P64"
  3287. DATA "MlA16P64A16P64MNA8P32MS>C8P16<MNB-16A8>C8<B-8A8P64MlA16P64A16P64MNA8P3MS>C8P16<MNB-16A8>C8<B-8A8P64"
  3288. DATA "A16A16MSA8MN>D2<A16A16MSA8MN>D2<A16A16MNA8MN>D2.D-32MlC64<B64B-64A64A-4G64G-64F64E64E-64"
  3289. DATA "T190>><<MlDP64D.G-8E-8MND8MlG-P64G-.A8G8MNG-8MlGP64G.B-8A8G8P64G-E-8P6E-8D.P16G-8P64G-E-8D8P64"
  3290. DATA "D8P64D8P8E-8P64E-D8C8P64C8P64C8P8CE-D16C8P64C8GP64G-E-8P64E-8DP4MNG2G2
  3291. DATA "MlG16P64G16P64MNG8P32MSB-8P16A16MNG8B-8A8G8P64MlG16P64G16P64MNG8P32MSB8P16A16MNG8B-8A8G8P64"
  3292. DATA "MlA16P64A16P64MNA8P32MS>C8P16<MNB-16A8>C8<B-8A8P64MlA16P64A16P64MNA8P3
  3293. DATA "mbl16T155o2mnb4p8mWillTell:.DATA "END_OF_SONG"
  3294. DATA "D64D-64C64<B64
  3295. '
  3296.  
  3297. ' From:  KELLY MUNDELL             Sent: 07-15-93 09:56
  3298. '   To:  TROY JONES                Rcvd: -NO-
  3299. '   Re:  A FEW QUESTIONS.
  3300. '
  3301. '-=- What did John say to Troy about A FEW QUESTIONS.?  Sheesh! -=-
  3302. '
  3303. ' TJ> Also, some QB code that has full editing? Like INS, DEL, backspace
  3304. ' TJ> editing, all that and such.  Anyone?  I didn't feel like having to deal
  3305. ' TJ> with all the string manipulation if I didn't have to.  Thanks.
  3306. '
  3307. 'Look at the following ::: a full editing input routine...
  3308.  
  3309. 'The following input routine, with full editing and DOS shell, was
  3310. 'written by myself years ago in GWBasic and has been enhanced through the
  3311. 'last few years by myself and programming partner James Parchen.  It is
  3312. 'released into this echo as public domain.  If you improve this routine,
  3313. 'please let me know.  Any comments and/or critisism would be greatly
  3314. 'appreciated.
  3315.  
  3316. DEFINT A-Z
  3317. SUB KeyIn (Ver$, Ln$, Mask$, Fg, Bg, p)
  3318.  
  3319. 'Ln$ = SPACE$(Number of Charecters to accept)
  3320. 'Ver$ = "ALL"          ' All Characters
  3321. 'Ver$ = "a-z"          ' Alpha Lower Case
  3322. 'Ver$ = "A-Z"          ' Alpha Upper Case
  3323. 'Ver$ = "a-Z"          ' Alpha Case off
  3324. 'Ver$ = "#'s"          ' Numbers Only
  3325. 'Mask$ = ""
  3326. 'Fg Foreground color
  3327. 'Bg Background color
  3328. 'p  Can't remember why this variable is in argument list but if it is 0
  3329. '   or 1 the routine should work fine.
  3330.  
  3331. DIM Chk(10)
  3332. IF Mask$ <> "" THEN
  3333.    Ln$ = Mask$
  3334.    FOR Chk = 1 TO LEN(Mask$)
  3335.       IF MID$(Mask$, Chk, 1) <> " " THEN Temp$ = Temp$ + STR$(Chk)
  3336.    NEXT Chk
  3337.    Mask$ = Temp$
  3338. END IF
  3339. S = POS(0): L = LEN(Ln$): COLOR Fg, Bg: PRINT Ln$; : IF p = 0 THEN p = 1
  3340.  
  3341. IF p > L THEN p = L + 1
  3342.  
  3343. LOCATE , S + p - 1, 1, 7, 7: Temp$ = ""
  3344.  
  3345. Alpha$ = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  3346. Num$ = " 0123456789"
  3347.  
  3348. IF UCASE$(LEFT$(Ver$, 4)) = "A-Z#" THEN Ver$ = Alpha$ + Num$ + MID$(Ver$, 5)
  3349.  
  3350. SELECT CASE LEFT$(Ver$, 3)
  3351.    CASE "ALL": Caps = 0: Ver$ = Alpha$ + Num$ + "!@#$%^&*()-_+=\[]{};':,./<>? "
  3352.    CASE "A-Z": Caps = 1: Ver$ = Alpha$ + MID$(Ver$, 4)
  3353.    CASE "a-z": Caps = 2: Ver$ = Alpha$ + MID$(Ver$, 4)
  3354.    CASE "a-Z": Caps = 0: Ver$ = Alpha$ + MID$(Ver$, 4)
  3355.    CASE "#'s": Caps = 0: Ver$ = Num$ + MID$(Ver$, 4)
  3356.    CASE ELSE: Caps = 0
  3357. END SELECT
  3358. a = 0: e = 0
  3359. WHILE a <> 13 AND a <> 27 AND a <> 10
  3360.    DO
  3361.       IF Caps = 0 THEN a$ = INKEY$
  3362.       IF Caps = 1 THEN a$ = UCASE$(INKEY$)
  3363.       IF Caps = 2 THEN a$ = LCASE$(INKEY$)
  3364.    LOOP UNTIL a$ <> ""
  3365.    a = ASC(a$): IF a = 0 THEN a = ASC(RIGHT$(a$, 1)) * -1
  3366.    p = POS(0) - S + 1: R = POS(0)
  3367.    'SCREEN , , 0, 0: COLOR 7, 0: CLS : PRINT a: END
  3368.      'Remove the above rem to see the value returned by variable `a' for
  3369.      'a specific key.  Extended keys are assigened a - value so we can
  3370.      'use just one variable for the keys.
  3371.  
  3372.    SELECT CASE a
  3373.       CASE -32                                            ' ALT-D For Dos Shell
  3374.          SCREEN , , 0, 0: CLS
  3375.          SHELL "Type EXIT [ENTER] To Return To Program"
  3376.          SHELL
  3377.       CASE -77: IF p < L + 1 THEN PRINT CHR$(28);  ELSE BEEP    ' Right arrow
  3378.       CASE -75: IF p <> 1 THEN PRINT CHR$(29);                  ' Left arrow
  3379.       CASE -71: LOCATE , S                                      ' <Home>
  3380.       CASE -119                                                 ' <Ctrl+Home>
  3381.          LOCATE , S: Ln$ = SPACE$(L): PRINT Ln$; : LOCATE , S
  3382.       CASE -79
  3383.          LOCATE , LEN(RTRIM$(Ln$)) + S                          ' <End>
  3384.       CASE -117                                                 ' <Ctrl+End>
  3385.          Ln$ = LEFT$(Ln$, p - 1) + SPACE$(L - p + 1)
  3386.          LOCATE , S, 0: PRINT Ln$; : LOCATE , R, 1
  3387.       CASE -116                                           ' <Ctrl+RightArrow>
  3388.          IF p <= L THEN
  3389.             Chk = INSTR(p, Ln$, " ")
  3390.             IF Chk <> 0 THEN
  3391.                Temp$ = LEFT$(LTRIM$(MID$(Ln$, Chk)), 1)
  3392.                IF Temp$ <> "" THEN LOCATE , S - 1 + INSTR(Chk, Ln$, Temp$), 1
  3393.             ELSE
  3394.                LOCATE , LEN(RTRIM$(Ln$)) + S
  3395.             END IF
  3396.          END IF
  3397.       CASE -115                                          ' <Ctrl+LeftArrow>
  3398.         Temp$ = RTRIM$(LEFT$(Ln$, p - 1))
  3399.         IF INSTR(Temp$, " ") THEN
  3400.            DO WHILE INSTR(Temp$, " ")
  3401.               Chk = INSTR(Temp$, " "): MID$(Temp$, Chk, 1) = "X"
  3402.            LOOP
  3403.            LOCATE , Chk + S, 1
  3404.         ELSE
  3405.            LOCATE , S
  3406.         END IF
  3407.       CASE 8                                              ' <Back Space>
  3408.          IF p <> 1 THEN
  3409.             Ln$ = LEFT$(Ln$, p - 2) + MID$(Ln$, p) + " "
  3410.             LOCATE , S, 0: PRINT Ln$; : LOCATE , R - 1, 1
  3411.          ELSE
  3412.             Ln$ = RIGHT$(Ln$, L - 1) + " ": LOCATE , S, 0: PRINT Ln$;
  3413.             LOCATE , p + S - 1, 1
  3414.          END IF
  3415.       CASE 127                                             ' <Ctrl+BckSpc>
  3416.          IF p > L THEN p = L
  3417.          Ln$ = SPACE$(p) + MID$(Ln$, p + 1)
  3418.          LOCATE , S, 0: PRINT Ln$; : LOCATE , R, 1
  3419.  
  3420.       CASE -83                                                  '<Delete>
  3421.          IF p <= L THEN
  3422.             Ln$ = LEFT$(Ln$, p - 1) + MID$(Ln$, p + 1) + " "
  3423.             LOCATE , S, 0: PRINT Ln$; : LOCATE , R, 1
  3424.          END IF
  3425.       CASE -82                                                 ' <Insert>
  3426.            IF insert = 0 THEN insert = 1 ELSE insert = 0
  3427.            IF insert = 0 THEN LOCATE , , 1, 7, 7
  3428.            IF insert = 1 THEN LOCATE , , 1, 4, 7
  3429.       CASE ELSE
  3430.          IF INSTR(Ver$, a$) AND p <= L THEN                 ' Print Character
  3431.             IF insert = 1 THEN
  3432.                Ln$ = LEFT$(Ln$, p - 1) + a$ + MID$(Ln$, p, L - p + 1)
  3433.                LOCATE , , 0: PRINT MID$(Ln$, p, L - p + 1); : LOCATE , R + 1, 1
  3434.             ELSE
  3435.                PRINT a$; : MID$(Ln$, p, 1) = a$
  3436.             END IF
  3437.             IF INSTR(Mask$, STR$(p + 1)) THEN PRINT MID$(Ln$, p + 1, 1);
  3438.          ELSE IF a <> 13 AND a <> 27 THEN BEEP
  3439.          END IF
  3440.       END SELECT
  3441.    WEND
  3442.    IF a = 27 THEN
  3443.       Ln$ = SPACE$(L)
  3444.       GOTO EndKeyIn
  3445.    END IF
  3446. EndKeyIn:
  3447. Ln$ = RTRIM$(Ln$)
  3448.  
  3449. END SUB
  3450. '
  3451.  
  3452.  
  3453.  From:  LOGAN ASHBY               Sent: 07-19-93 16:55
  3454.    To:  TOM TREMAIN               Rcvd: -NO-
  3455.    Re:  HELP 1/2
  3456.  
  3457. Answering a msg of <13 Jul 93>, from Tom Tremain to All:
  3458.  
  3459.  TT> I'm trying to figure out a couple of things in QB, I could
  3460.  TT> use some help.
  3461.  
  3462.  TT> 1)  How to tell if a drive is
  3463.  TT>         a) a subst drive
  3464.  TT>         b) network drive
  3465.  TT>         c) ram drive
  3466.  
  3467.     I'm not even sure exacty what question 2 was asking, but here's some
  3468. code to handle #1.  This has at least one known bug in that it gives
  3469. incorrect results if you have a drive letter subst'ed to a directory on
  3470. a floppy.  Its method of determining phantom drives ain't exactly
  3471. bulletproof, either ;-). Maybe it'll give you a place to start, anyway.
  3472.  
  3473.  '---------------- begin code -----------------------
  3474.  
  3475.  ' --------------------------- TRUNAME.BAS ---------------------------
  3476.  '|  Checks for physical and logical drives on a system, returns      |
  3477.  '|  the truename for SUBSTed and JOINed drives                       |
  3478.  '|  Logan Ashby - 04/17/92 - Released to Public Domain               |
  3479.  ' -------------------------------------------------------------------
  3480.  '$INCLUDE: 'QBX.BI'
  3481.  DEFINT A-Z
  3482.  
  3483.  DECLARE FUNCTION IsRemoveable% (DrvNum%)
  3484.  DECLARE SUB TrueName (DrvNum%, DrvType%, RemappedTo$)
  3485.  
  3486.  CONST FALSE = 0
  3487.  CONST TRUE = -1
  3488.  CONST PHYSICAL = 0                  '| Constants for drive type
  3489.  CONST LOGICAL = 1                   '|  possibilities.
  3490.  CONST NETWORK = 2
  3491.  CONST PHANTOM = 3
  3492.  CONST RAMDRV = 4
  3493.  
  3494.  DIM SHARED RegsX AS RegTypeX
  3495.  COMMON SHARED Null$
  3496.  
  3497.  Null$ = CHR$(0)
  3498.  
  3499.  CLS
  3500.  RegsX.ax = &H1900                   '| Get starting crnt drive
  3501.  CALL InterruptX(&H21, RegsX, RegsX)
  3502.  CurDrv% = RegsX.ax
  3503.  
  3504.  FOR i% = 0 TO 25
  3505.      RegsX.ax = &HE00                '| Set CurrentDrive
  3506.      RegsX.dx = i%
  3507.      CALL InterruptX(&H21, RegsX, RegsX)
  3508.  
  3509.      RegsX.ax = &H1900               '| Did it take?
  3510.      CALL InterruptX(&H21, RegsX, RegsX)
  3511.  
  3512.      IF ((RegsX.ax AND &HFF) = i%) THEN
  3513.      ' ----------------------------------------------------------
  3514.      '| OK, we got a valid drive letter,  lets see what type of  |
  3515.      '|   drive it is.                                           |
  3516.      ' ----------------------------------------------------------
  3517.          DrvLtr$ = CHR$(i% + 65)
  3518.          PRINT "Drive "; DrvLtr$; " is: ";
  3519.          CALL TrueName(i%, DrvType%, RemappedTo$)
  3520.          SELECT CASE DrvType%
  3521.          CASE PHYSICAL:
  3522.              PRINT " Physical Drive "
  3523.  
  3524.          CASE LOGICAL:
  3525.              PRINT " Logical Drive : "; RemappedTo$
  3526.  
  3527.          CASE NETWORK:
  3528.              PRINT " Network Drive : "; RemappedTo$
  3529.  
  3530.          CASE PHANTOM:
  3531.              PRINT " Phantom Drive : "; RemappedTo$
  3532.  
  3533.          CASE RAMDRV:
  3534.              PRINT " Ram Drive"
  3535.  
  3536.          END SELECT
  3537.      END IF
  3538.  NEXT i%
  3539.  
  3540.  RegsX.ax = &HE00                    '| Restore original Drive
  3541.  RegsX.dx = CurDrv%
  3542.  CALL InterruptX(&H21, RegsX, RegsX)
  3543.  
  3544.  END
  3545.  
  3546.  ' --------------------------------------------------------------
  3547.  '| We need to test for a floppy with this method, as the        |
  3548.  '|  truename function will poll the hardware, and generate a    |
  3549.  '|  crit. error if no floppy in drive/door open                 |
  3550.  ' --------------------------------------------------------------
  3551.  FUNCTION IsRemoveable% (DrvNum%)
  3552.      RegsX.ax = &H4408
  3553.      RegsX.bx = DrvNum% + 1
  3554.      CALL InterruptX(&H21, RegsX, RegsX)
  3555.      IF (RegsX.Flags AND 1) THEN
  3556.          ' ------------------------------------------------------
  3557.          '| Could handle this error better. For now, just assume |
  3558.          '|   it's not a floppy, as this code generates an error |
  3559.          '|   for a Novell mapped drive.                         |
  3560.          ' ------------------------------------------------------
  3561.          IsRemoveable% = FALSE
  3562.      ELSE
  3563.          IF (RegsX.ax) THEN
  3564.              IsRemoveable% = FALSE
  3565.          ELSE
  3566.              IsRemoveable% = TRUE
  3567.          END IF
  3568.      END IF
  3569.  END FUNCTION
  3570.  
  3571.  ' --------------------------------------------------------------
  3572.  '| Get the true name of the drive letter, using Int &H21,       |
  3573.  '|  Function &H60.  Available since DOS 3.0, but not documented |
  3574.  '|  until 5.0                                                   |
  3575.  '| Modified 7/19/93 to detect ramdisks, phantom drives, and     |
  3576.  '|  network drives.                                             |
  3577.  ' --------------------------------------------------------------
  3578.  SUB TrueName (DrvNum%, DrvType%, ReMapped$)
  3579.      DIM DrvPath AS STRING * 128
  3580.      IF (IsRemoveable%(DrvNum%)) THEN
  3581.          ' ------------------------------------------------------
  3582.          '| Check for a phantom drive.  The assumption is that   |
  3583.          '|  if the drive letter is A: or B: and the system only |
  3584.          '|  has one floppy, the other is a phantom drive.  We   |
  3585.          '|  check the BIOS to see which letter is currently     |
  3586.          '|  being used to access the hardware.                  |
  3587.          ' ------------------------------------------------------
  3588.          DEF SEG = 0
  3589.          NumFlpys% = ((PEEK(&H410) AND &HC0) \ 64) + 1
  3590.          IF (NumFlpys% = 1) THEN
  3591.              CrntFlpy% = PEEK(&H504)
  3592.              IF ((DrvNum% < 2) AND (CrntFlpy% <> DrvNum%)) THEN
  3593.                  DrvType% = PHANTOM
  3594.                  ReMapped$ = _
  3595.                      "Mapped to floppy hardware when requested"
  3596.              ELSE
  3597.                  DrvType% = PHYSICAL
  3598.                  ReMapped$ = ""
  3599.              END IF
  3600.          ELSE
  3601.              DrvType% = PHYSICAL
  3602.              ReMapped$ = ""
  3603.          END IF
  3604.          DEF SEG
  3605.      ELSE                            '| The period after the
  3606.                                      '|  backslash here avoids a
  3607.                                      '|  Novell bug
  3608.          DrvLtr$ = CHR$(DrvNum% + 65) + ":\." + Null$
  3609.          DrvPath = DrvLtr$           '| TrueName function
  3610.          RegsX.ax = &H6000
  3611.          RegsX.di = VARPTR(DrvPath)  '| We use the same buffer
  3612.          RegsX.si = RegsX.di         '|  for the input and
  3613.          RegsX.ds = VARSEG(DrvPath)  '|  output
  3614.          RegsX.es = RegsX.ds
  3615.          CALL InterruptX(&H21, RegsX, RegsX)
  3616.          DrvLtr$ = LEFT$(DrvLtr$, 3)
  3617.          TmpPath$ = LEFT$(DrvPath, INSTR(DrvPath, Null$) - 1)
  3618.          IF (DrvLtr$ <> TmpPath$) THEN
  3619.              IF (INSTR(DrvPath, "\\")) THEN
  3620.                  DrvType% = NETWORK
  3621.              ELSE
  3622.                  DrvType% = LOGICAL
  3623.              END IF
  3624.              ReMapped$ = TmpPath$
  3625.          ELSE
  3626.              RegsX.ax = &H3200       '| Get Drive Parameter
  3627.              RegsX.dx = DrvNum% + 1  '|  Block for this drive
  3628.              CALL InterruptX(&H21, RegsX, RegsX)
  3629.              DEF SEG = RegsX.ds      '| Get the # of FAT's
  3630.              NumFATS% = PEEK(RegsX.bx + 8)
  3631.              DEF SEG
  3632.              IF (NumFATS% = 1) THEN  '| A non-removeable drive w/
  3633.                  DrvType% = RAMDRV   '|  only one FAT is usually
  3634.  
  3635.              ELSE                    '|  a RAM drive.
  3636.                  DrvType% = PHYSICAL
  3637.              END IF
  3638.              ReMapped$ = ""
  3639.          END IF
  3640.      END IF
  3641.  END SUB
  3642.  
  3643. '
  3644.  
  3645.  From:  CHAD GUNTER               Sent: 07-20-93 22:12
  3646.    To:  BILL NORCROSS             Rcvd: -NO-
  3647.    Re:  SOFTWARE RESET
  3648.  
  3649.    Bill, I had that same question some time ago - I got two responses
  3650. and I'll give 'em to ya:
  3651.  
  3652.  
  3653. 'Date: 04-0793 (04:29)
  3654. Number: 24943 of 25295 '  To: CHAD GUNTER
  3655.      Read: 04-11-93 (21:14) 'Subj: REBOOT                       
  3656.  
  3657.  
  3658. 'CG> Does anyone know how to make the computer reboot from inside QB? Thanx!
  3659. 'Here you go:
  3660. '============================== Begin code==============================
  3661. DEFINT A-Z
  3662. '$INCLUDE: 'qb.bi'
  3663. 'replace with qbx.bi if you're using PDS
  3664. DECLARE SUB Reboot (BootType%)
  3665. call reboot(0) 'warm boot. NON-ZERO is cold boot.
  3666. '**********************************************************************
  3667. '* SUB Reboot
  3668. '*
  3669. '*    Reboots the machine by executing the machine language procedure
  3670. '*    located at FFFF:0000.
  3671. '*
  3672. '* EXTERNAL ROUTINE(S)
  3673. '*    QBX.LIB
  3674. '*    -------
  3675. '*    SUB Absolute (Address%)
  3676. '**********************************************************************
  3677.  SUB Reboot (BootType%) STATIC
  3678.    DEF SEG = &H40
  3679.    POKE &H72, 0 - ((BootType% = 0) * &H34)   'if BootType% = 0, perform
  3680.    POKE &H73, 0 - ((BootType% = 0) * &H12)   '  a warm boot, else a cold boot
  3681.    DEF SEG = &HFFFF                          'set segment to bootstrap loader
  3682.    Absolute 0                                'do system call END SUB
  3683. '=============================== End code===============================
  3684.  
  3685. 'Set BootType% = 0 to perform a warm boot, or anything else to perform a
  3686. 'cold boot. '
  3687.  
  3688.  
  3689. 'CG>Does anyone know how to make the computer reboot from inside QB?
  3690. '======================================================================
  3691. 'From: ZACK JONES
  3692. '> I search how to entry a command for REBOOT the computer  in QB45.
  3693. 'Try this: 'From PC Magazine, 17 Mar 92, Page 439
  3694. DECLARE SUB
  3695. ReBoot(Warm%) CALL ReBoot(1) 'Save this program before running it!
  3696.  
  3697. SUB ReBoot (Warm%) STATIC
  3698.   IF Warm% THEN              'if they want a warm boot
  3699.     DEF SEG = 0              'assign the value 1234 Hex
  3700.     POKE &H473, &H12         'to address 0000:0472 Hex
  3701.     POKE &H472, &H34
  3702.   END IF
  3703.   DEF SEG = &HFFFF           'either way call the BIOS
  3704.   CALL Absolute(0)           'routine at FFFF:0000 Hex END SUB
  3705.  
  3706. END SUB
  3707. '
  3708. ' From:  VICTOR YIU                Sent: 07-22-93 14:19
  3709. '   To:  ALL                       Rcvd: -NO-
  3710. '   Re:  POSTIT! 7.0 ANNOUCEMENT!
  3711. '
  3712. '+----- Here's the PostIt! 7.0 everybody's been waiting for! -------
  3713. '|    To:  ALL
  3714. '|  From:  Rich Geldreich
  3715. '|  Subj:  PostIt! 7.0
  3716. '+-----------------------------------
  3717. '    The following 10 messages contain the source code for PostIt! v7.1.
  3718. '
  3719. '
  3720. '    This version was rewritten totally from scratch, by me. It's now a
  3721. 'command line utility, like it should of been from the beginning.
  3722. '
  3723. '    I feel pretty sure that no major bugs exist in this version. I spent
  3724. 'a lot of time debugging & testing it. If you find any bugs, please
  3725. 'inform Victor Yiu or me and we'll fix them up.
  3726. '
  3727. '    The first message contains instructions about how to use this
  3728. 'version of PostIt! If something still remains fuzzy after you read this,
  3729. 'please ask either Victor or me and we'll clear things up for you.
  3730. '
  3731. ''>>> Page 1 of PI71.BAS begins here. TYPE:BAS
  3732. ' PostIt! v7.1 Script Encoder/Decoder-Public Domain-July 1993
  3733. ' By Rich Geldreich & Victor Yiu. Many  contributions,   fixups,  and
  3734. ' features by Mark H. Butler,  Quinn Tyler Jackson, and Scott Wunsch.
  3735. '
  3736. ' PostIt! can  encode   any  binary   file  into  a  series  of self-
  3737. ' extracting  script  files  that  can  be  reliably  distributed  on
  3738. ' text-only  conferences  or  networks.   The  script  files  can  be
  3739. ' extracted with  this  program,  or  with  any  Microsoft QuickBASIC
  3740. ' language (DOS 5's QBASIC, QB4.5, PDS, VB-DOS) because  each  script
  3741. ' contains its own small QuickBASIC decoder.
  3742. '
  3743. ' PostIt!   can  also  format  QuickBASIC  source  code  suitable for
  3744. ' distribution on conferences, and reconstruct source code  formatted
  3745. ' by  this  program.   This  allows  QuickBASIC programmers to easily
  3746. ' exchange BASIC source code without worrying about the annoying line
  3747. ' length and message limitations of most networks.
  3748. '
  3749. ' New 7.1 Features
  3750. '
  3751. ' o  Totally rewritten source code!
  3752. ' o  Much  more efficient  encoding algorithm (MOD 86 encoding)  with
  3753. '    a smaller and faster self extractor!
  3754. ' o  Huge binary scripts now supported, up to 150k!
  3755. ' o  The  script decoding & unfiltering functions  are now automated!
  3756. '    As  long  as a few  simple rules are followed (see the  notes on
  3757. '    the Decode command), no  user intervention  is needed to extract
  3758. '    multiple scripts from the same capture file.
  3759. ' o  PostIt!  is  finally  a command line utility! Error codes can be
  3760. '    returned  to batch  files if  you're compiling   with  VBDOS  or
  3761. '    QBX.   Look  at the source to  find out  which error  code means
  3762. '    which.
  3763. ' o  The format of PostIt!'s   message  headers has finally been well
  3764. '    thought out and (hopefully) finalized.  Although   compatibility
  3765. '    with  previous versions of PostIt!  has been sacrificed, scripts
  3766. '    created by  newer versions  of  PostIt!   should be decodable by
  3767. '    this version because of a common message header format.
  3768. '
  3769. ' Explanation of Commands
  3770. '
  3771. ' E = Encodes  any binary  file less than 150k into a self-extracting
  3772. '     text-only script.  If the -s  option is used with this command,
  3773. '     the entire script will be written to one output file; otherwise
  3774. '     the script will be split into multiple output files, where each
  3775. '     output file contains one message.   (Note:  Scripts created  by
  3776. '     this  command  cannot  be  extracted  by  previous  versions of
  3777. '     PostIt!.)
  3778. '
  3779. ' F = Filters QuickBASIC source code for  posting  on  a  conference.
  3780. '     This  command  actually  performs  two filtering functions.  It
  3781. '     splits very long  lines  with  continuation characters (special
  3782. '     precautions are taken to ensure  quoted strings and remarks are
  3783. '     split correctly), and chops the source code into multiple files
  3784. '     so each file corresponds to one message  (unless the -s  option
  3785. '     is used).The filtered file can still be executed or compiled by
  3786. '     QuickBASIC, just as the original could.  (Note: DATA statements
  3787. '     split by filtering cannot be unsplit correctly by QB! This will
  3788. '     hopefully  be fixed  soon...   Files  filtered  by this command
  3789. '     cannot by unfiltered by previous versions of PostIt!.)
  3790. '
  3791. ' D = Decodes binary/text scripts.  Multiple scripts can  be  decoded
  3792. '     from the same  input  file  with  this  function.  The decoding
  3793. '     algorithm  automatically  decides  which  method  was  used  to
  3794. '     encode the source file(binary script or source code filtering).
  3795. '
  3796. '     If  any  errors  are  encountered during decoding the script is
  3797. '     skipped  and the  partly decoded  file is deleted.
  3798. '
  3799. '     Binary and text scripts created by previous versions of PostIt!
  3800. '     cannot be decoded with this command, because of the new  header
  3801. '     format employed by this version of PostIt!.
  3802. '
  3803. '     (Notes:  Pages of a script MUST appear in increasing order.  In
  3804. '     other words, page 2 must follow page 1, page 3 must follow page
  3805. '     2, etc.  When posting  files  created  by  the E or F commands,
  3806. '     don't modify or remove the message headers because the decoding
  3807. '     algorithm expects these to indicate the beginning and ending of
  3808. '     each page.  (All message headers begin with a "'>>>" sequence.)
  3809. '     Finally, if an output file is specified on  the  command  line,
  3810. '     for  example "POSTIT D capture.txt c:\q\coolcode.zip", only the
  3811. '     specified output file  (COOLCODE.ZIP  in  the  example) will be
  3812. '     decoded if its script can be  located.   The  pathname  of  the
  3813. '     output  file  will  be  the  destination  path specified on the
  3814. '     command line.  In the  example,  the  file COOLCODE.ZIP will be
  3815. '     written to the C:\Q directory.)
  3816. '
  3817. ' Completely stupid and irrelevant examples for the Average Fool
  3818. '
  3819. ' postit e maim.zip -p95 -b20 c:\scripts\mc
  3820.  
  3821. ' (Encodes a binary script of MAIM.ZIP. All output file(s) are written
  3822. '  to the C:\SCRIPTS directory and begin with the "MC" suffix. The
  3823. '  message length is 95 lines, and 20 blank lines are reserved on the
  3824. '  first message.)
  3825. ' postit -a f x-ray.bas -o -s
  3826. ' (Filters the file X-RAY.BAS for posting. All blank lines are padded
  3827. '  with a space, no prompting is done for file overwrites, and no
  3828. '  message splitting is performed.)
  3829. ' postit d zebra.txt q\
  3830. ' (Decodes all scripts from the file ZEBRA.TXT to the Q directory.)
  3831. '
  3832. DEFINT A-Z
  3833. DECLARE FUNCTION Decode% (oSwitch%, InSpec$, OutSpec$)
  3834. DECLARE FUNCTION Encode% (Op%, iSwitch%, cSwitch%, aSwitch%, tSwitch%, sSwitch%, pSwitch%, lSwitch%, oSwitch%, bSwitch%, InSpec$, OutSpec$)
  3835. DECLARE SUB ExpandLine (A$, Lines$(), LineLength%, NumLines%)
  3836. DECLARE FUNCTION FASC% (A$)
  3837. DECLARE FUNCTION GrabNum& (A$, Lower&, Upper&, Default&)
  3838. DECLARE SUB ParseCmdLine (Cmd$, Params$(), Found%)
  3839. DECLARE SUB SepPath (A$, Drive$, path$, tName$)
  3840. DECLARE FUNCTION UnTab$ (B$, TabStops%)
  3841.  
  3842.  
  3843. CONST True = -1, False = 0, Debug% = False
  3844. DIM SHARED GERR%: ON ERROR GOTO ErrHandler
  3845. LOCATE , , 1
  3846. PRINT "PostIt! v7.1 QuickBASIC Compatible Encoder/Decoder"
  3847. PRINT "Public Domain by Rich Geldreich and Victor Yiu"
  3848. PRINT
  3849. IF FRE(-1) < 65536 THEN ErrLvl% = 1: PRINT "Not enough memory": GOTO AllDone
  3850. DIM Params$(1 TO 10)
  3851. 'The following line must be modified for DOS 5 QBASIC.
  3852. ParseCmdLine COMMAND$, Params$(), NumParams%
  3853. IF NumParams% = 0 THEN ErrLvl% = 2: GOTO ShowHelp
  3854. FOR I% = 1 TO NumParams%
  3855.   Q$ = Params$(I%)
  3856.   IF LEFT$(Q$, 1) <> "-" AND LEN(Q$) = 1 THEN
  3857.     Command% = INSTR("EFD", Q$)
  3858.     IF Command% <> 0 THEN
  3859.       Params$(I%) = "": EXIT FOR
  3860.     ELSE
  3861.       PRINT "Bad command: "; Q$: PRINT : ErrLvl% = 3: GOTO ShowHelp
  3862.     END IF
  3863.   END IF
  3864. NEXT
  3865. IF Command% = 0 THEN PRINT "No command specified.": PRINT : ErrLvl% = 4: GOTO ShowHelp
  3866. IF Command% = 2 THEN DefaultLineLength% = 72 ELSE DefaultLineLength% = 65
  3867. sSwitch% = False: pSwitch% = 85: lSwitch% = DefaultLineLength%
  3868. tSwitch% = 4: oSwitch% = False: bSwitch% = 0: aSwitch% = False
  3869. iSwitch% = False: cSwitch% = False
  3870. FOR I% = 1 TO NumParams%
  3871.   Q$ = Params$(I%): Z$ = MID$(Q$, 3)
  3872.   IF LEN(Q$) THEN
  3873.     IF LEFT$(Q$, 1) = "-" THEN
  3874.       SELECT CASE MID$(Q$, 2, 1)
  3875.       CASE "S": sSwitch% = True
  3876.       CASE "P": pSwitch% = GrabNum&(Z$, 45, 1000, 85)
  3877.       CASE "L": lSwitch% = GrabNum&(Z$, 60, 80, CLNG(DefaultLineLength%))
  3878.       CASE "T": tSwitch% = GrabNum&(Z$, 1, 8, 4)
  3879.       CASE "O": oSwitch% = True
  3880.       CASE "B": bSwitch% = GrabNum&(Z$, 0, 30, 0)
  3881.       CASE "A": aSwitch% = True
  3882.       CASE "I": iSwitch% = True
  3883.       CASE "C": cSwitch% = True
  3884.       CASE ELSE: PRINT "Bad switch: "; Q$: PRINT : ErrLvl% = 3: GOTO ShowHelp
  3885.       END SELECT
  3886.     ELSE
  3887.       SELECT CASE J%
  3888.       CASE 0: InputSpec$ = Q$
  3889.       CASE 1: OutputSpec$ = Q$
  3890.       CASE ELSE: PRINT "Too many filenames.": PRINT : ErrLvl% = 5:
  3891. GOTO ShowHelp
  3892.       END SELECT: J% = J% + 1
  3893.     END IF
  3894.   END IF
  3895. NEXT
  3896. IF J% < 1 THEN PRINT "Must specify input file.": PRINT : ErrLvl% = 5: GOTO ShowHelp
  3897. SepPath InputSpec$, InputDrive$, InputPath$, InputName$
  3898. IF INSTR(InputName$, ".") = 0 THEN
  3899.   IF Command% = 1 THEN     'encoding  .ZIP
  3900.     InputSpec$ = InputSpec$ + ".ZIP"
  3901.   ELSEIF Command% = 2 THEN 'filtering .BAS
  3902.     InputSpec$ = InputSpec$ + ".BAS"
  3903.   ELSEIF Command% = 3 THEN 'decoding  .TXT
  3904.     InputSpec$ = InputSpec$ + ".TXT"
  3905.   END IF
  3906. ELSE
  3907.   IF Command% = 1 THEN
  3908.     SELECT CASE MID$(InputName$, INSTR(InputName$, ".") + 1, 3)
  3909.     CASE "ZIP", "LZH", "ARJ", "GIF", "SQZ", "ZOO", "ARC", "HAP", "JPG"
  3910.     CASE ELSE: PRINT "Warning: Uncompressed files should not be encoded" + " into binary scripts!": PRINT
  3911.   END SELECT
  3912.   END IF
  3913. END IF
  3914. OPEN InputSpec$ FOR INPUT AS #1: CLOSE #1
  3915. IF GERR% THEN PRINT "Can't open "; InputSpec$: ErrLvl% = 6: GOTO AllDone
  3916. SepPath OutputSpec$, OutDrive$, OutPath$, OutName$
  3917. TestFile$ = OutDrive$ + OutPath$ + "pi742875.2yz"
  3918. OPEN TestFile$ FOR OUTPUT AS #1: CLOSE #1
  3919. IF GERR% THEN PRINT "Bad output specification.": ErrLvl% = 7: GOTO AllDone
  3920. KILL TestFile$
  3921. SELECT CASE Command%
  3922. CASE 1: Status% = Encode%(0, iSwitch%, cSwitch%, aSwitch%, tSwitch%, sSwitch%, pSwitch%, lSwitch%, oSwitch%, bSwitch%, InputSpec$, OutputSpec$)
  3923. CASE 2: Status% = Encode%(1, iSwitch%, cSwitch%, aSwitch%, tSwitch%, sSwitch%, pSwitch%, lSwitch%, oSwitch%, bSwitch%, InputSpec$, OutputSpec$)
  3924. CASE 3: Status% = Decode%(oSwitch%, InputSpec$, OutputSpec$)
  3925. END SELECT
  3926. IF Status% < 0 THEN ErrLvl% = 8 ELSE IF Status% > 0 THEN ErrLvl% = 9 ELSE ErrLvl% = 0
  3927. GOTO AllDone
  3928. ShowHelp:
  3929. PRINT "Usage: POSTIT [switch] command inputfile [outputfile]"
  3930. PRINT
  3931. PRINT "Commands:"
  3932. PRINT "e [E]ncode any file <150k into a self extracting binary script"
  3933. PRINT "f [F]ilter QB source into a text script"
  3934. PRINT "d [D]ecode captured text or binary script(s)"
  3935. PRINT
  3936. PRINT "Switches:"
  3937. PRINT "-s  Don't split output file into multiple messages"
  3938. PRINT "-o  Don't prompt for file overwrites"
  3939. PRINT "-b# Reserve # blank lines on first message (0-30, default=0)"
  3940. PRINT "-t# Set tab stops to # characters (1-8, default=4)"
  3941. PRINT "-l# Set line length to # characters (60-80, default=65 or 72)"
  3942. PRINT "-p# Set message length to # lines (45-1000, default=85)"
  3943. PRINT "-a  Padd blank lines with a space when filtering"
  3944. PRINT "-i  Ignore blank lines when filtering"
  3945. PRINT "-c  Crush space characters from start of lines when filtering"
  3946. AllDone:
  3947. IF Debug% THEN PRINT "Exiting with an errorlevel of"; ErrLvl%
  3948. END ErrLvl% 'Change this to just "END" if running with QB4.5
  3949. ErrHandler: GERR% = ERR
  3950.   IF Debug% THEN IF GERR% <> 53 THEN PRINT "Global error #"; GERR%
  3951. RESUME NEXT
  3952.  
  3953. '
  3954.  
  3955. FUNCTION Decode% (oSwitch%, InSpec$, OutSpec$)
  3956.   DIM Lines$(1 TO 256), ValidChar%(255)
  3957.   FOR Q% = 0 TO 85 'Valid encoding characters
  3958.     IF Q% = 27 THEN
  3959.       ValidChar%(ASC("#")) = True
  3960.     ELSEIF Q% = 59 THEN
  3961.       ValidChar%(ASC("$")) = True
  3962.     ELSE
  3963.       ValidChar%(Q% + 37) = True
  3964.     END IF
  3965.   NEXT
  3966.   GERR% = 0: Z$ = "OPEN " + CHR$(34) + "O" + CHR$(34) + ",1," + CHR$(34)
  3967.   SepPath OutSpec$, OutDrive$, OutPath$, OutName$
  3968.   OutPath$ = OutDrive$ + OutPath$
  3969.   InputHandle% = FREEFILE
  3970.   OPEN InSpec$ FOR INPUT AS InputHandle% LEN = 8192
  3971.   OutputHandle% = FREEFILE
  3972.   DO
  3973.     IF FoundNewScript% = False THEN
  3974.       DO UNTIL EOF(InputHandle%)
  3975.         M% = M% + 1: IF M% = 16 THEN GOSUB AbortCheck
  3976.         LineNum& = LineNum& + 1
  3977.         LINE INPUT #1, A$: A$ = LTRIM$(RTRIM$(UCASE$(A$)))
  3978.         IF GERR% THEN PRINT "Error while reading from input file!": GOTO DecodeExit
  3979.         IF LEFT$(A$, 14) = "'>>> PAGE 1 OF" AND INSTR(A$, "BEGINS" + " HERE") > 0 AND INSTR(A$, "TYPE:") > 0 THEN EXIT DO
  3980.       LOOP
  3981.       IF EOF(InputHandle%) THEN EXIT DO
  3982.     END IF
  3983.     FoundNewScript% = False
  3984.     OutFile$ = LTRIM$(MID$(A$, 15))
  3985.     OutFile$ = RTRIM$(LEFT$(OutFile$, INSTR(OutFile$, "BEGINS") - 1))
  3986.     IF LEN(OutFile$) = 0 THEN GOTO FindNext
  3987.     IF LEN(OutName$) = 0 OR OutFile$ = OutName$ THEN
  3988.       FilesCRC% = -1: FilesLength& = -1: ScrDone% = False
  3989.       BadScript% = False: NumLines% = 0: K% = 0: s% = 0: B& = 0
  3990.       Q% = INSTR(A$, "TYPE:") + 5
  3991.       SELECT CASE MID$(A$, Q%, 3)
  3992.       CASE "BAS": ScriptType% = 0
  3993.       CASE "BIN"
  3994.         ScriptType% = 1
  3995.         EncodeVer% = FASC%(MID$(A$, Q% + 3, 1)) - 65
  3996.         ExtractVer% = FASC%(MID$(A$, Q% + 4, 1)) - 65
  3997.         IF ExtractVer% <> 0 THEN PRINT "Unsupported encoding algorithm" + " for file "; OutFile$: PRINT : GOTO FindNext
  3998.       CASE ELSE: PRINT "Unsupported script type for file "; OutFile$: PRINT : GOTO FindNext
  3999.       END SELECT
  4000.       GOSUB CheckLine
  4001.       OPEN OutPath$ + OutFile$ FOR INPUT AS OutputHandle%: CLOSE OutputHandle%
  4002.       IF GERR% = 0 THEN
  4003.         IF oSwitch% = False THEN
  4004.           PRINT OutPath$ + OutFile$; " already exists. [O]verwrite, or" + " [A]bort(o/a)? ";
  4005.           DO: DO: A$ = INKEY$: LOOP UNTIL LEN(A$): A$ = UCASE$(A$)
  4006.           LOOP UNTIL INSTR("OA" + CHR$(27), A$)
  4007.           LOCATE , 1: PRINT SPACE$(78); : LOCATE , 1
  4008.           SELECT CASE A$
  4009.           CASE "A", CHR$(27): GERR% = -1: PRINT "Aborted by user!": GOTO DecodeExit
  4010.           END SELECT
  4011.         END IF
  4012.       END IF
  4013.       GERR% = 0: OPEN OutPath$ + OutFile$ FOR OUTPUT AS OutputHandle%
  4014.       IF GERR% THEN PRINT "Error while opening "; OutPath$ + OutFile$; "!": GOTO DecodeExit
  4015.       OutSpecOpened% = True
  4016.       IF ScriptType% = 0 THEN PRINT "Unfiltering ";  ELSE PRINT "Decoding ";
  4017.       PRINT OutPath$ + OutFile$; "... ";
  4018.       LookingForNextPage% = False
  4019.       CurrentPage% = 1
  4020.       DO UNTIL EOF(InputHandle%)
  4021.         IF GERR% THEN PRINT "Error #"; STR$(GERR%); " while processing" + " file!": GOTO DecodeExit
  4022.         M% = M% + 1: IF M% = 16 THEN GOSUB AbortCheck
  4023.         LineNum& = LineNum& + 1
  4024.         LINE INPUT #InputHandle%, A$: A$ = RTRIM$(A$)
  4025.         IF ScriptType% = 1 THEN A$ = LTRIM$(A$)
  4026.         IF LEFT$(A$, 4) = "'>>>" THEN
  4027.           GOSUB CheckLine
  4028.           IF UCASE$(LEFT$(A$, 10)) = "'>>> PAGE " THEN
  4029.             A$ = UCASE$(A$)
  4030.             IF LEFT$(A$, 15) = "'>>> PAGE 1 OF " AND INSTR(A$, "BEGINS" + " HERE") > 0 THEN
  4031.               PRINT "Premature end of script on line"; LineNum&
  4032.               FoundNewScript% = True: BadScript% = True: EXIT DO
  4033.             END IF
  4034.             IF GrabNum&(MID$(A$, 11), 1, 256, -1) <> CurrentPage% THEN PRINT "Page out of sync on line"; LineNum&: BadScript% = True: EXIT DO
  4035.             IF INSTR(A$, "BEGINS HERE") THEN
  4036.               IF LookingForNextPage% = False THEN PRINT "Page"; CurrentPage%; " was encountered more than once on line"; LineNum&: BadScript% = True: EXIT DO
  4037.               LookingForNextPage% = False
  4038.             ELSEIF INSTR(A$, "ENDS HERE") THEN
  4039.               IF LookingForNextPage% = True THEN PRINT "Page"; CurrentPage%; "was terminated prematurely on line"; LineNum&: BadScript% = True: EXIT DO
  4040.               LookingForNextPage% = True
  4041.               CurrentPage% = CurrentPage% + 1
  4042.               IF INSTR(A$, "LAST PAGE") THEN ScrDone% = True: EXIT DO
  4043.             ELSE
  4044.               PRINT "Bad page header on line"; LineNum&: BadScript% = True: EXIT DO
  4045.             END IF
  4046.           END IF
  4047.         ELSE
  4048.           IF LookingForNextPage% = False THEN
  4049.             IF ScriptType% = 0 THEN
  4050.               GOSUB ShrinkLine
  4051.             ELSE
  4052.               IF LEFT$(A$, 1) = "U" AND LEFT$(LTRIM$(MID$(A$, 2)), 1) = CHR$(34) THEN GOSUB DecodeLine
  4053.             END IF
  4054.           END IF
  4055.         END IF
  4056.       LOOP
  4057.       IF BadScript% = False THEN
  4058.         IF ScrDone% = False THEN PRINT "Premature end of script on" + " line"; LineNum&: BadScript% = True: GOTO DecodeDone
  4059.         GoodScripts% = GoodScripts% + 1
  4060.         IF ScriptType% = 0 THEN
  4061.           IF NumLines% > 0 THEN A$ = "": GOSUB ShrinkLine
  4062.           PRINT "Ok"
  4063.         ELSE
  4064.           IF FilesLength& = -1 THEN
  4065.             PRINT "Warning: File's length could not be located!"
  4066.           ELSEIF FilesLength& <> B& THEN
  4067.             PRINT "Warning: Decoded file's length is incorrect."
  4068.           ELSEIF FilesCRC% = -1 THEN
  4069.             PRINT "Warning: File's checksum could not be located!"
  4070.           ELSEIF FilesCRC% <> s% THEN
  4071.             PRINT "Warning: Decoded file's checksum is incorrect."
  4072.           ELSE
  4073.             PRINT "Ok"
  4074.           END IF
  4075.         END IF
  4076.       END IF
  4077. DecodeDone:
  4078.       CLOSE OutputHandle%
  4079.       IF GERR% THEN PRINT "Error while writing to output file!": GOTO DecodeExit
  4080.       IF BadScript% THEN KILL OutPath$ + OutFile$
  4081.       OutSpecOpened% = False
  4082.       PRINT : IF OutFile$ = OutName$ THEN EXIT DO
  4083.     END IF
  4084. FindNext:
  4085.   LOOP UNTIL EOF(InputHandle%)
  4086. '----------------------------------------------------------
  4087. DecodeExit:
  4088.   Q% = GERR%: CLOSE InputHandle%: CLOSE OutputHandle%
  4089.   IF Q% = 0 THEN PRINT LTRIM$(STR$(GoodScripts%)); " script(s) decoded" + " successfully."
  4090.   IF Q% <> 0 AND OutSpecOpened% THEN KILL OutPath$ + OutFile$
  4091.   Decode% = Q%
  4092. EXIT FUNCTION
  4093. '----------------------------------------------------------
  4094. ShrinkLine:
  4095.   FoundIt% = FASC(RIGHT$(A$, 1)) = 95
  4096.   IF FoundIt% THEN
  4097.     InQuote% = False
  4098.     FOR I% = 1 TO LEN(A$)
  4099.       IF MID$(A$, I%, 1) = CHR$(34) THEN InQuote% = NOT InQuote%
  4100.     NEXT
  4101.     'Don't combine lines that are part of binary scripts
  4102.     IF InQuote% THEN FoundIt% = False
  4103.   END IF
  4104.   IF FoundIt% OR NumLines% > 0 THEN
  4105.     IF NumLines% = 256 THEN
  4106.       PRINT "Too many line continuations!": BadScript% = True: GOTO DecodeDone
  4107.     END IF
  4108.     NumLines% = NumLines% + 1: Lines$(NumLines%) = A$
  4109.     IF FoundIt% = False THEN 'last line?
  4110.       A$ = ""
  4111.       FOR A% = 1 TO NumLines%
  4112.         B$ = Lines$(A%)
  4113.         'can we combine two quoted strings together?
  4114.         CombineQuote% = False
  4115.         IF RIGHT$(A$, 2) = "+_" AND LEN(A$) > 3 THEN
  4116.           IF RIGHT$(RTRIM$(LEFT$(A$, LEN(A$) - 2)), 1) = CHR$(34) THEN
  4117.             IF FASC(LTRIM$(B$)) = 34 THEN CombineQuote% = True
  4118.           END IF
  4119.         END IF
  4120.         IF CombineQuote% THEN
  4121.           A$ = RTRIM$(LEFT$(A$, LEN(A$) - 2))
  4122.           A$ = LEFT$(A$, LEN(A$) - 1) + MID$(LTRIM$(B$), 2)
  4123.         ELSE
  4124.           InQuote% = False
  4125.           'can we combine two remarks together?
  4126.           FOR I% = 1 TO LEN(A$)
  4127.             Q$ = MID$(A$, I%, 1)
  4128.             IF Q$ = CHR$(34) THEN
  4129.               InQuote% = NOT InQuote%
  4130.             ELSEIF InQuote% = False THEN
  4131.               IF Q$ = "'" OR UCASE$(MID$(A$, I%, 4)) = "REM " THEN
  4132.                 IF LEFT$(LTRIM$(B$), 1) = "'" THEN B$ = MID$(B$, 2)
  4133.                 EXIT FOR
  4134.               END IF
  4135.             END IF
  4136.           NEXT
  4137.           'eradicate trailing "_" character
  4138.           IF LEN(A$) THEN A$ = LEFT$(A$, LEN(A$) - 1)
  4139.           A$ = A$ + B$
  4140.         END IF
  4141.       NEXT
  4142.       PRINT #OutputHandle%, A$: NumLines% = 0
  4143.     END IF
  4144.   ELSE
  4145.     PRINT #OutputHandle%, A$
  4146.   END IF
  4147.   IF GERR% THEN PRINT "Error while writing to output file!": GOTO DecodeExit
  4148. RETURN
  4149.  
  4150. DecodeLine: '**MOD 86 Decoder**
  4151.   A$ = MID$(LTRIM$(MID$(A$, 2)), 2)
  4152.   IF RIGHT$(A$, 1) = CHR$(34) THEN A$ = LEFT$(A$, LEN(A$) - 1)
  4153.   FOR A% = 1 TO LEN(A$)
  4154.     C% = ASC(MID$(A$, A%, 1))
  4155.     IF ValidChar%(C%) = False THEN PRINT "Illegal character found on" + " line"; LineNum&: BadScript% = True: GOTO DecodeDone
  4156.     C% = C% - 37: IF C% < 0 THEN C% = 91 + C% * 32
  4157.     IF K% < 4 THEN
  4158.       IF C% > 80 THEN PRINT "Decode out of sync/illegal character found" + " on line"; LineNum&: BadScript% = True: GOTO DecodeDone
  4159.       K% = C% + 243
  4160.     ELSE
  4161.       T% = C% + (K% MOD 3) * 86: IF T% > 255 THEN PRINT "Illegal" + " character found on line"; LineNum&: BadScript% = True: GOTO DecodeDone
  4162.       PRINT #OutputHandle%, CHR$(T%);
  4163.       IF GERR% THEN PRINT "Error while writing to output file!": GOTO DecodeExit
  4164.       B& = B& + 1: K% = K% \ 3
  4165.     END IF
  4166.     s% = (s% + C%) AND 255
  4167.   NEXT
  4168. RETURN
  4169. '----------------------------------------------------------
  4170. CheckLine:
  4171.   Q% = INSTR(A$, "TLEN:")
  4172.   IF Q% THEN FilesLength& = GrabNum&(MID$(A$, Q% + 5), 1, 153600, -1)
  4173.   Q% = INSTR(A$, "TCHK:")
  4174.   IF Q% THEN FilesCRC% = GrabNum&(MID$(A$, Q% + 5), 0, 255, -1)
  4175. RETURN
  4176. '----------------------------------------------------------
  4177. AbortCheck: M% = 0: K$ = INKEY$
  4178.   IF K$ = CHR$(27) OR K$ = CHR$(0) + CHR$(0) THEN GERR% = -1: PRINT "Aborted by user!": GOTO DecodeExit
  4179. RETURN
  4180. END FUNCTION
  4181.  
  4182. FUNCTION Encode% (Op%, iSwitch%, cSwitch%, aSwitch%, tSwitch%, sSwitch%, pSwitch%, lSwitch%, oSwitch%, bSwitch%, InSpec$, OutSpec$)
  4183.   DIM OutputFile$(1 TO 256), Bucket%(1 TO 4), Lines$(64)
  4184.   GERR% = 0: Q$ = CHR$(34)
  4185. '----------------------------------------------------------
  4186.   SepPath InSpec$, OutDrive$, OutPath$, InName$
  4187.   SepPath OutSpec$, OutDrive$, OutPath$, OutName$
  4188.   IF LEN(OutName$) = 0 THEN
  4189.     OutName$ = InName$
  4190.     IF INSTR(OutName$, ".") THEN OutName$ = LEFT$(OutName$, INSTR(OutName$, ".") - 1)
  4191.   END IF
  4192.   IF INSTR(OutName$, ".") THEN
  4193.     OutExt$ = MID$(OutName$, INSTR(OutName$, "."))
  4194.     OutName$ = LEFT$(OutName$, INSTR(OutName$, ".") - 1)
  4195.   END IF
  4196.   IF LEN(OutExt$) = 0 THEN IF Op% THEN OutExt$ = ".PST" ELSE OutExt$ = ".PI"
  4197. '----------------------------------------------------------
  4198.   InputHandle% = FREEFILE
  4199.   IF Op% THEN
  4200.     OPEN InSpec$ FOR INPUT AS InputHandle% LEN = 8192
  4201.   ELSE
  4202.     OPEN InSpec$ FOR BINARY AS InputHandle%
  4203.   END IF
  4204.   InputFileSize& = LOF(InputHandle%)
  4205.   IF Op% = 0 AND InputFileSize& > (150 * 1024&) THEN
  4206.     PRINT "Can't encode files larger than 150k."
  4207.     GERR% = -1: GOTO EncodeExit
  4208.   ELSEIF InputFileSize& = 0 THEN
  4209.     PRINT "Input file is null.": GERR% = -2: GOTO EncodeExit
  4210.   END IF
  4211. '----------------------------------------------------------
  4212.   IF Op% THEN PRINT "Filtering ";  ELSE PRINT "Encoding ";
  4213.   PRINT InSpec$; " ("; LTRIM$(STR$((InputFileSize& + 1023) \ 1024)); "k)"
  4214.   PRINT
  4215. '----------------------------------------------------------
  4216.   OutputHandle% = FREEFILE: LinesInPage% = 0
  4217. '----------------------------------------------------------
  4218.   IF Op% = 0 THEN
  4219.     Work$ = "U" + Q$ + SPACE$(lSwitch% - 2): WorkPos% = 3
  4220.     CurrentSub% = 0: LinesInSub% = 0: FlagScaler% = 1
  4221.     GOSUB PrintDecodeHeader
  4222.     BytesLeft& = InputFileSize&: BufferSize% = 4096
  4223.     Buffer$ = SPACE$(BufferSize)
  4224.     DO
  4225.       IF GERR% THEN PRINT "- Error #"; STR$(GERR%); " while encoding" + " file!": GOTO EncodeExit
  4226.       IF BytesLeft& < BufferSize% THEN Buffer$ = SPACE$(BytesLeft&): BufferSize% = BytesLeft&
  4227.       GET InputHandle%, , Buffer$
  4228.       IF GERR% THEN PRINT "- Error while reading from input file!": GOTO EncodeExit
  4229.       GOSUB EncodeBlock
  4230.     LOOP WHILE BytesLeft&
  4231.     IF NumCodes% THEN GOSUB FlushCodeBuffer
  4232.     IF WorkPos% > 3 THEN Work$ = LEFT$(Work$, WorkPos% - 1): GOSUB PutSubLine
  4233.     IF LinesInSub% THEN L$ = "END SUB": GOSUB PutLine
  4234.     FOR A% = 2 TO CurrentSub%: L$ = "V" + HEX$(A%): GOSUB PutLine: NEXT
  4235.     GOSUB PrintDecodeTrailer
  4236.   ELSE
  4237.     BytesLeft& = InputFileSize&
  4238.     DO UNTIL EOF(InputHandle)
  4239.       IF GERR% THEN PRINT "- Error #"; STR$(GERR%); " while filtering" + " file!": GOTO EncodeExit
  4240.       LINE INPUT #InputHandle, A$: A$ = RTRIM$(UnTab$(A$, tSwitch%))
  4241.       IF GERR% THEN PRINT "- Error while reading from input file!": GOTO EncodeExit
  4242.       IF cSwitch% THEN A$ = LTRIM$(A$)
  4243.       BytesLeft& = BytesLeft& - LEN(A$) - 2
  4244.       IF LEN(A$) > 0 OR iSwitch% = False THEN
  4245.         ExpandLine A$, Lines$(), lSwitch%, NumLines%
  4246.         'Don't let split lines cross page boundries, because QB won't
  4247.         'put them back together.
  4248.         IF sSwitch% = False AND (NumLines% > 1) AND (LinesInPage% + 1 + NumLines%) > pSwitch% THEN
  4249.           PRINT #OutputHandle%, "'>>> Page"; STR$(NumOutputFiles%); " of "; InName$; " ends here. Continued on next page."
  4250.           LinesInPage% = LinesInPage% + 1: GOSUB CloseOutputFile
  4251.         END IF
  4252.         FOR A% = 1 TO NumLines%
  4253.           L$ = Lines$(A%)
  4254.           'Don't let blank lines proceed the first page header.
  4255.           IF LinesInPage% <> 0 OR LEN(RTRIM$(L$)) > 0 THEN
  4256.             'The padding option is for those unfortunates that post
  4257.             'source online in RBBS's grubby line oriented text editor...
  4258.             IF aSwitch% THEN IF LEN(L$) = 0 THEN L$ = " "
  4259.             GOSUB PutLine
  4260.           END IF
  4261.         NEXT
  4262.       END IF
  4263.     LOOP
  4264.   END IF
  4265. '----------------------------------------------------------
  4266.   L$ = "'>>> Page" + STR$(NumOutputFiles%) + " of " + InName$ + "ends" + " here. Last page."
  4267.   IF Op% = 0 THEN L$ = L$ + " TCHK:" + LTRIM$(STR$(CheckSum%))
  4268.   GOSUB PutLine: GOSUB CloseOutputFile: PRINT
  4269.   PRINT LTRIM$(STR$(TotalLines%)); " lines in"; STR$(NumOutputFiles%); " message(s) written."
  4270. '----------------------------------------------------------
  4271. EncodeExit:
  4272.   Q% = GERR%
  4273.   CLOSE InputHandle%: CLOSE OutputHandle%
  4274.   IF Q% <> 0 THEN FOR A% = 1 TO NumOutputFiles%: KILL OutputFile$(A%): NEXT
  4275.   Encode% = Q%
  4276. EXIT FUNCTION
  4277. '----------------------------------------------------------
  4278. EncodeBlock: '**MOD 86 Encoder**
  4279.   FOR I% = 1 TO BufferSize%
  4280.     Byte% = ASC(MID$(Buffer$, I%, 1)): BytesLeft& = BytesLeft& - 1
  4281.     CurrentFlag% = CurrentFlag% + (Byte% \ 86) * FlagScaler%
  4282.     FlagScaler% = FlagScaler% * 3: NumCodes% = NumCodes% + 1
  4283.     Bucket%(NumCodes%) = Byte% MOD 86
  4284.     IF NumCodes% = 4 THEN GOSUB FlushCodeBuffer
  4285.   NEXT
  4286. RETURN
  4287. '----------------------------------------------------------
  4288. FlushCodeBuffer:
  4289.   Q% = CurrentFlag%: GOSUB PutByte
  4290.   FOR J% = 1 TO NumCodes%: Q% = Bucket%(J%): GOSUB PutByte: NEXT
  4291.   NumCodes% = 0: CurrentFlag% = 0: FlagScaler% = 1
  4292. RETURN
  4293. '----------------------------------------------------------
  4294. PutByte:
  4295.   CheckSum% = (CheckSum% + Q%) AND 255
  4296.   IF Q% = 27 THEN
  4297.     MID$(Work$, WorkPos%) = "#"
  4298.   ELSEIF Q% = 59 THEN
  4299.     MID$(Work$, WorkPos%) = "$"
  4300.   ELSE
  4301.     MID$(Work$, WorkPos%) = CHR$(Q% + 37)
  4302.   END IF
  4303.   WorkPos% = WorkPos% + 1: IF WorkPos% > lSwitch% THEN GOSUB PutSubLine
  4304. RETURN
  4305. '----------------------------------------------------------
  4306. PutSubLine:
  4307.   IF LinesInSub% = 0 THEN
  4308.     CurrentSub% = CurrentSub% + 1
  4309.     IF CurrentSub% = 1 THEN
  4310.       L$ = "SUB V1:OPEN " + Q$ + "O" + Q$ + ",1," + Q$ + InName$ + Q$ + ",4^6:Z&=" + LTRIM$(STR$(LOF(1))) + ":?STRING$(50,177);"
  4311.     ELSE
  4312.       L$ = "SUB V" + HEX$(CurrentSub%)
  4313.     END IF
  4314.     GOSUB PutLine
  4315.   END IF
  4316.   L$ = Work$: GOSUB PutLine
  4317.   LinesInSub% = LinesInSub% + 1
  4318.   IF LinesInSub% = 200 THEN L$ = "END SUB": GOSUB PutLine:
  4319. LinesInSub% = 0
  4320.   WorkPos% = 3
  4321. RETURN
  4322. '----------------------------------------------------------
  4323. PutLine:
  4324.   IF LinesInPage% = 0 THEN GOSUB OpenNewOutputFile
  4325.   PRINT #OutputHandle%, L$
  4326.   IF GERR% THEN PRINT "- Error writing to output file!": GOTO EncodeExit
  4327.   LinesInPage% = LinesInPage% + 1
  4328.   IF sSwitch% = False THEN
  4329.     'make sure last page has some meat on it
  4330.     IF LinesInPage% = (pSwitch% - 1) OR (BytesLeft& < 256 AND LinesInPage% > (pSwitch% - 10)) THEN
  4331.       PRINT #OutputHandle%, "'>>> Page"; STR$(NumOutputFiles%); " of"; InName$; " ends here. Continued on next page."
  4332.       LinesInPage% = LinesInPage% + 1: GOSUB CloseOutputFile
  4333.     END IF
  4334.   END IF
  4335.   'Check the blower for contol+c and escape every few lines...
  4336.   IF (LinesInPage% AND 7) = 1 THEN
  4337.     A$ = INKEY$: IF A$ = CHR$(27) OR A$ = CHR$(0) + CHR$(0) THEN GERR% = -3: PRINT "- Aborted by user!": GOTO EncodeExit
  4338.   END IF
  4339. RETURN
  4340. '----------------------------------------------------------
  4341. OpenNewOutputFile:
  4342.   IF NumOutputFiles% = 256 THEN GERR% = -4: PRINT "Too many output" + " files!": GOTO EncodeExit
  4343.   NumOutputFiles% = NumOutputFiles% + 1
  4344.   IF sSwitch% = True THEN
  4345.     J$ = OutName$
  4346.   ELSE
  4347.     J$ = LTRIM$(STR$(NumOutputFiles%))
  4348.     J$ = LEFT$(OutName$, 8 - LEN(J$)) + J$
  4349.   END IF
  4350.   OutFile$ = OutDrive$ + OutPath$ + J$ + OutExt$: GERR% = 0
  4351.   OPEN OutFile$ FOR INPUT AS OutputHandle%: CLOSE OutputHandle%
  4352.   IF GERR% = 0 THEN
  4353.     IF oSwitch% = False THEN
  4354.       PRINT OutFile$; " already exists. [O]verwrite, overwrite [R]est," + " or [A]bort(o/r/a)? ";
  4355.       DO: DO: A$ = INKEY$: LOOP UNTIL LEN(A$): A$ = UCASE$(A$)
  4356.       LOOP UNTIL INSTR("ORA" + CHR$(27), A$)
  4357.       LOCATE , 1: PRINT SPACE$(78); : LOCATE , 1
  4358.       SELECT CASE A$
  4359.       CASE "A", CHR$(27): GERR% = -3: PRINT "Aborted by user!"
  4360.         NumOutputFiles% = NumOutputFiles% - 1: GOTO EncodeExit
  4361.       CASE "R": oSwitch% = True
  4362.       END SELECT
  4363.     END IF
  4364.   END IF
  4365.   PRINT "Now writing: "; OutFile$; " ";
  4366.   GERR% = 0: OPEN OutFile$ FOR OUTPUT AS OutputHandle% LEN = 4096
  4367.   OutputFile$(NumOutputFiles%) = OutFile$
  4368.   IF GERR% THEN
  4369.     PRINT "- Error opening output file!"
  4370.     NumOutputFiles% = NumOutputFiles% - 1: GOTO EncodeExit
  4371.   END IF
  4372.   LinesInPage% = 1
  4373.   IF NumOutputFiles% = 1 THEN
  4374.     FOR I% = 1 TO bSwitch%
  4375.       IF aSwitch% THEN PRINT #OutputHandle, " " ELSE PRINT #OutputHandle,
  4376.     NEXT
  4377.     LinesInPage% = LinesInPage% + bSwitch%
  4378.   END IF
  4379.   PRINT #OutputHandle%, "'>>> Page"; STR$(NumOutputFiles%); " of "; InName$; " begins here.";
  4380.   IF NumOutputFiles% > 1 THEN
  4381.     PRINT #OutputHandle%,
  4382.   ELSE
  4383.     IF Op% = 0 THEN
  4384.       'The first letter after "BIN" is  which algorithm was used
  4385.       'to encode the file. The second letter is the minimum decoding
  4386.       'algorithm required to extract the file. Both range from A-Z.
  4387.       PRINT #OutputHandle%, " TYPE:BINAA";
  4388.       'TLEN stands for "total length".
  4389.       PRINT #OutputHandle%, " TLEN:"; LTRIM$(STR$(InputFileSize&))
  4390.       'In the future, other information may be put onto this line,
  4391.       'such as the file's date and time. (Actually, any line
  4392.       'starting will "'>>>" will be scanned for information by
  4393.       'the Decode function.)
  4394.     ELSE
  4395.       PRINT #OutputHandle%, " TYPE:BAS"
  4396.     END IF
  4397.   END IF
  4398.   GERR% = 0
  4399. RETURN
  4400. '----------------------------------------------------------
  4401. CloseOutputFile:
  4402.   CLOSE OutputHandle%
  4403.   IF GERR% THEN PRINT "- Error while writing to output file!": GOTO EncodeExit
  4404.   PRINT : TotalLines% = TotalLines% + LinesInPage%: LinesInPage% = 0
  4405. RETURN
  4406. '----------------------------------------------------------
  4407. PrintDecodeHeader:
  4408.   L$ = "DEFINT A-Z:DIM SHARED K,S,B&,Z&:V1 'Created by PostIt! 7.1"
  4409.   GOSUB PutLine
  4410. RETURN
  4411. '----------------------------------------------------------
  4412. PrintDecodeTrailer:
  4413.   L$ = "CLOSE:IF S=" + LTRIM$(STR$(CheckSum%))
  4414.   L$ = L$ + "AND B&=Z&THEN?" + Q$ + " :) Ok!" + Q$ + "ELSE?" + Q$ + "" + ":( Bad!"
  4415.   GOSUB PutLine
  4416.   L$ = "SUB U(A$):FOR A=1TO LEN(A$):C=ASC(MID$(A$,A))-37:IF C<0THEN" + " C=91+C*32"
  4417.   GOSUB PutLine
  4418.   L$ = "IF K<4THEN K=C+243ELSE?#1,CHR$(C+(K MOD 3)*86);:K=K\3:B&=B&+1"
  4419.   GOSUB PutLine
  4420.   L$ = "S=(S+C)AND 255:NEXT:LOCATE,1:?STRING$(B&*50\Z&,219);:END SUB"
  4421.   GOSUB PutLine
  4422. RETURN
  4423. END FUNCTION
  4424.  
  4425. 'This self containted subroutine for splitting QB lines was made by
  4426. 'Victor Yiu and a few other folks on the QUIK_BAS echo.
  4427. SUB ExpandLine (A$, Lines$(), LineLength%, NumLines%)
  4428.   NumLines% = 0
  4429.   'check to see if the line has already been split
  4430.   FOR I% = LEN(A$) TO 1 STEP -1
  4431.     SELECT CASE MID$(A$, I%, 1)
  4432.     CASE "_": NoSplit% = True
  4433.     CASE " "
  4434.     CASE ELSE: EXIT FOR
  4435.     END SELECT
  4436.   NEXT
  4437.   DO WHILE NoSplit% = False AND LEN(A$) > LineLength%
  4438.     'locate a place to split the line
  4439.     WrapPoint% = 0
  4440.     FOR I% = LineLength% TO LineLength% - 20 STEP -1
  4441.       SELECT CASE MID$(A$, I%, 1)
  4442.       CASE " ", ".", ",", ":", ";": WrapPoint% = I%: EXIT FOR
  4443.       END SELECT
  4444.     NEXT
  4445.     IF WrapPoint% = 0 THEN WrapPoint% = LineLength%
  4446.     'avoid wrapping on quote chars
  4447.     IF MID$(A$, WrapPoint% - 1, 1) = CHR$(34) THEN WrapPoint% = WrapPoint% - 1
  4448.     InQuote% = False: HasComment% = False
  4449.     'check to see if the line contains a remark
  4450.     FOR I% = 1 TO WrapPoint% - 1
  4451.       Q$ = MID$(A$, I%, 1)
  4452.       IF Q$ = CHR$(34) THEN
  4453.         InQuote% = NOT InQuote%
  4454.       ELSEIF InQuote% = False THEN
  4455.         IF Q$ = "'" OR UCASE$(MID$(A$, I%, 4)) = "REM " THEN
  4456.           HasComment% = True: EXIT FOR
  4457.         END IF
  4458.       END IF
  4459.     NEXT
  4460.     NumLines% = NumLines% + 1
  4461.     IF InQuote% THEN
  4462.       Lines$(NumLines%) = LEFT$(A$, WrapPoint% - 1) + CHR$(34) + "+_"
  4463.     ELSE
  4464.       Lines$(NumLines%) = LEFT$(A$, WrapPoint% - 1) + "_"
  4465.     END IF
  4466.     A$ = MID$(A$, WrapPoint%)
  4467.     IF HasComment% THEN
  4468.       A$ = "'" + A$
  4469.     ELSEIF InQuote% THEN
  4470.       A$ = CHR$(34) + A$
  4471.     END IF
  4472.   LOOP
  4473.   NumLines% = NumLines% + 1: Lines$(NumLines%) = A$
  4474. END SUB
  4475.  
  4476. FUNCTION FASC% (A$)
  4477.   IF LEN(A$) = 0 THEN FASC% = -1 ELSE FASC% = ASC(A$)
  4478. END FUNCTION
  4479.  
  4480. FUNCTION GrabNum& (A$, Lower&, Upper&, Default&)
  4481.   FOR I% = 1 TO LEN(A$)
  4482.     Q$ = MID$(A$, I%, 1): IF (Q$ < "0" OR Q$ > "9") THEN EXIT FOR
  4483.     J& = J& * 10& + ASC(Q$) - 48
  4484.     IF J& > Upper& THEN GrabNum& = Default&: EXIT FUNCTION
  4485.   NEXT
  4486.   GrabNum& = J&: IF LEN(A$) = 0 OR J& < Lower& OR J& > Upper& THEN GrabNum& = Default&
  4487. END FUNCTION
  4488.  
  4489. 'This parsing sub does NOT mistake filenames like "F-14G.ZIP" as
  4490. 'containing a switch. That's why it looks so big.
  4491. SUB ParseCmdLine (Cmd$, Params$(), Found%)
  4492.   Found% = 0: Sep$ = "-/": Temp$ = LTRIM$(RTRIM$(Cmd$)): InParam% = 0
  4493.   FOR p% = 1 TO LEN(Temp$)
  4494.     C$ = MID$(Temp$, p%, 1)
  4495.     IF InParam% = -1 THEN 'Inside of a switch?
  4496.       IF INSTR(Sep$, C$) THEN 'Found another switch?
  4497.         'Terminate current switch, then start parsing the next one.
  4498.         GOSUB MakeParam: MID$(Temp$, p%, 1) = LEFT$(Sep$, 1)
  4499.         ParamStart% = p%
  4500.       ELSEIF ASC(C$) = 32 OR ASC(C$) = 9 THEN
  4501.         GOSUB MakeParam: InParam% = 0 'Terminate current switch.
  4502.       END IF
  4503.     ELSEIF InParam% = -2 THEN 'Inside of a parameter?
  4504.       IF ASC(C$) = 32 OR ASC(C$) = 9 THEN 'Terminate parameter with
  4505.         GOSUB MakeParam: InParam% = 0     'space or TAB.
  4506.       END IF
  4507.     ELSE
  4508.       IF INSTR(Sep$, C$) THEN 'Found start of a switch?
  4509.         'Make sure all switches start with "-".
  4510.         MID$(Temp$, p%, 1) = LEFT$(Sep$, 1): InParam% = -1
  4511.         ParamStart% = p%
  4512.       ELSEIF ASC(C$) <> 32 AND ASC(C$) <> 9 THEN 'If char isn't a
  4513.         InParam% = -2: ParamStart% = p% 'space or TAB it's a parameter.
  4514.       END IF
  4515.     END IF
  4516.   NEXT
  4517.   IF InParam% THEN GOSUB MakeParam
  4518.   EXIT SUB
  4519. MakeParam:
  4520.   Found% = Found% + 1
  4521.   Params$(Found%) = MID$(Temp$, ParamStart%, p% - ParamStart%)
  4522.   IF Found% = UBOUND(Params$) THEN EXIT SUB
  4523. RETURN
  4524. END SUB
  4525.  
  4526. SUB SepPath (A$, Drive$, path$, tName$)
  4527.   FOR I% = LEN(A$) TO 1 STEP -1
  4528.     IF INSTR("\:", MID$(A$, I%, 1)) THEN EXIT FOR
  4529.   NEXT
  4530.   IF I% > 0 THEN
  4531.     path$ = UCASE$(MID$(A$, 1, I%)): tName$ = UCASE$(MID$(A$, I% + 1))
  4532.   ELSE
  4533.     path$ = "": tName$ = UCASE$(A$)
  4534.   END IF
  4535.   Temp% = INSTR(path$, ":"): Drive$ = ""
  4536.   IF Temp% THEN Drive$ = LEFT$(path$, Temp%): path$ = MID$(path$, Temp% + 1)
  4537. END SUB
  4538.  
  4539. FUNCTION UnTab$ (B$, TabStops%)
  4540.   A$ = B$: T% = INSTR(A$, CHR$(9))
  4541.   IF T% THEN
  4542.     DO: Temp% = (T% - 1) MOD TabStops%
  4543.     A$ = LEFT$(A$, T% - 1) + SPACE$(TabStops% - Temp%) + MID$(A$, T% + 1)
  4544.     T% = INSTR(T%, A$, CHR$(9)): LOOP WHILE T%
  4545.   END IF
  4546.   UnTab$ = A$
  4547. END FUNCTION '(last subroutine)
  4548.  
  4549. '
  4550.